mirror of
https://github.com/bckelley/cgm-remote-monitor.git
synced 2026-08-24 03:14:12 -05:00
Merge branch 'dev' into 181121-minorLgGER
This commit is contained in:
@@ -265,8 +265,6 @@ To learn more about the Nightscout API, visit https://YOUR-SITE.com/api-docs.htm
|
||||
* `SHOW_RAWBG` (`never`) - possible values `always`, `never` or `noise`
|
||||
* `CUSTOM_TITLE` (`Nightscout`) - Usually name of T1
|
||||
* `THEME` (`default`) - possible values `default`, `colors`, or `colorblindfriendly`
|
||||
* `INSECURE_USE_HTTP` (`false`) - possible values `false`, or `true`.
|
||||
* `SECURE_HTTP_HEADERS` (`false`) - possible values `false`, or `true`.
|
||||
* `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`
|
||||
@@ -281,7 +279,14 @@ To learn more about the Nightscout API, visit https://YOUR-SITE.com/api-docs.htm
|
||||
* 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`. Enable or disable icon allowing enter treatments edit mode
|
||||
|
||||
### Views
|
||||
### Predefined values for your server settings (optional)
|
||||
* `INSECURE_USE_HTTP` (`false`) - Redirect http url's to https. Possible values `false`, or `true`.
|
||||
* `SECURE_HSTS_HEADER` (`true`) - Add HTTP Strict Transport Security (HSTS) header. Possible values `false`, or `true`.
|
||||
* `SECURE_HSTS_HEADER_INCLUDESUBDOMAINS` (`false`) - includeSubdomains options for HSTS. Possible values `false`, or `true`.
|
||||
* `SECURE_HSTS_HEADER_PRELOAD` (`false`) - ask for preload in browsers for HSTS. Possible values `false`, or `true`.
|
||||
* `SECURE_CSP` (`false`) - Add Content Security Policy headers. Possible values `false`, or `true`. Currently Nightscout is not yet compatible with CSP.
|
||||
|
||||
### Views
|
||||
|
||||
There are a few alternate web views available that display a simplified BG stream. Append any of these to your Nightscout URL:
|
||||
* `/clock.html` - Shows current BG. Grey text on a black background.
|
||||
|
||||
@@ -14,22 +14,36 @@ function create(env, ctx) {
|
||||
var appInfo = env.name + ' ' + env.version;
|
||||
app.set('title', appInfo);
|
||||
app.enable('trust proxy'); // Allows req.secure test on heroku https connections.
|
||||
if (process.env.INSECURE_USE_HTTP !== 'true') {
|
||||
if (!env.settings.isEnabled('insecureUseHttp')) {
|
||||
app.use((req, res, next) => {
|
||||
if (req.header('x-forwarded-proto') !== 'https')
|
||||
res.redirect(`https://${req.header('host')}${req.url}`)
|
||||
res.redirect(`https://${req.header('host')}${req.url}`);
|
||||
else
|
||||
next()
|
||||
})
|
||||
if (process.env.SECURE_HTTP_HEADERS == 'true') {
|
||||
const helmet = require('helmet')
|
||||
//if (env.settings.isEnabled('secureHstsHeader')) { // by TODO: find out why env.settings.isEnabled doest not work
|
||||
if (process.env.SECURE_HSTS_HEADER == 'true') { // Add HSTS (HTTP Strict Transport Security) header
|
||||
const helmet = require('helmet');
|
||||
var includeSubDomainsValue = process.env.SECURE_HSTS_HEADER_INCLUDESUBDOMAINS || false ; // _get(env, 'extendedSettings.secureHstsHeader.includesubdomains')
|
||||
var preloadValue = process.env.SECURE_HSTS_HEADER_PRELOAD || false; // _get(env, 'extendedSettings.secureHstsHeader.preload') || false ; // default
|
||||
app.use(helmet({
|
||||
hsts: {
|
||||
maxAge: 31536000,
|
||||
includeSubDomains: true,
|
||||
preload: true
|
||||
includeSubDomains: includeSubDomainsValue,
|
||||
preload: preloadValue
|
||||
}
|
||||
}))
|
||||
//if (env.settings.isEnabled('secureCsp')) { // Add Content-Security-Policy directive by default
|
||||
if (process.env.SECURE_CSP == 'true') {
|
||||
app.use(helmet.contentSecurityPolicy({ // TODO make NS work without 'unsafe-inline'
|
||||
directives: {
|
||||
defaultSrc: ["'self'"],
|
||||
styleSrc: ["'self'", 'https://fonts.googleapis.com/',"'unsafe-inline'"],
|
||||
scriptSrc: ["'self'", "'unsafe-inline'"],
|
||||
fontSrc: [ "'self'", 'https://fonts.gstatic.com/']
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -151,6 +151,31 @@
|
||||
"description": "Maker Announcement Key - Set this to your secret key for announcements Note for additional info see https://github.com/nightscout/cgm-remote-monitor/blob/dev/README.md#ifttt-maker , maker should be added to enable if you want to use maker Leave blank if not using maker",
|
||||
"value": "",
|
||||
"required": false
|
||||
},
|
||||
"INSECURE_USE_HTTP": {
|
||||
"description": "If set to true or unspecified the site will redirect requests over http to https. If set to false requests over http will not be redirected and served insecurely. Default: false",
|
||||
"value": "false",
|
||||
"required": false
|
||||
},
|
||||
"SECURE_HSTS_HEADER": {
|
||||
"description": "If set to true or unspecified HSTS (HTTP Strict Transport Security) header will be added. Default: true",
|
||||
"value": "true",
|
||||
"required": false
|
||||
},
|
||||
"SECURE_HSTS_HEADER_INCLUDESUBDOMAINS": {
|
||||
"description": "If set to true `includeSubDomains` will be added to the HSTS (HTTP Strict Transport Security) header. This header is required for `preload` Default: false",
|
||||
"value": "false",
|
||||
"required": false
|
||||
},
|
||||
"SECURE_HSTS_HEADER_PRELOAD": {
|
||||
"description": "If set to true `preload` will be added to the HSTS (HTTP Strict Transport Security) header. This will add your site to the HSTS preloaded list. This features requires SECURE_HSTS_HEADER_INCLUDESUBDOMAINS to be true. Default: false",
|
||||
"value": "false",
|
||||
"required": false
|
||||
},
|
||||
"SECURE_CSP": {
|
||||
"description": "If set to true a Content Security Policy header will be added. Default: true",
|
||||
"value": "true",
|
||||
"required": false
|
||||
}
|
||||
},
|
||||
"addons": [
|
||||
|
||||
@@ -92,20 +92,6 @@ function setVersion() {
|
||||
function setStorage() {
|
||||
env.storageURI = readENV('STORAGE_URI') || readENV('MONGO_CONNECTION') || readENV('MONGO') || readENV('MONGOLAB_URI') || readENV('MONGODB_URI');
|
||||
env.entries_collection = readENV('ENTRIES_COLLECTION') || readENV('MONGO_COLLECTION', 'entries');
|
||||
env.MQTT_MONITOR = readENV('MQTT_MONITOR', null);
|
||||
if (env.MQTT_MONITOR) {
|
||||
var hostDbCollection = [env.storageURI.split('mongodb://').pop().split('@').pop(), env.entries_collection].join('/');
|
||||
var mongoHash = crypto.createHash('sha1');
|
||||
mongoHash.update(hostDbCollection);
|
||||
//some MQTT servers only allow the client id to be 23 chars
|
||||
env.mqtt_client_id = mongoHash.digest('base64').substring(0, 23);
|
||||
console.info('Using Mongo host/db/collection to create the default MQTT client_id', hostDbCollection);
|
||||
if (env.MQTT_MONITOR.indexOf('?clientId=') === -1) {
|
||||
console.info('Set MQTT client_id to: ', env.mqtt_client_id);
|
||||
} else {
|
||||
console.info('MQTT configured to use a custom client id, it will override the default: ', env.mqtt_client_id);
|
||||
}
|
||||
}
|
||||
env.authentication_collections_prefix = readENV('MONGO_AUTHENTICATION_COLLECTIONS_PREFIX', 'auth_');
|
||||
env.treatments_collection = readENV('MONGO_TREATMENTS_COLLECTION', 'treatments');
|
||||
env.profile_collection = readENV('MONGO_PROFILE_COLLECTION', 'profile');
|
||||
|
||||
+11
-44
@@ -7,7 +7,6 @@ var _includes = require('lodash/includes');
|
||||
|
||||
var consts = require('../../constants');
|
||||
var es = require('event-stream');
|
||||
var sgvdata = require('sgvdata');
|
||||
var expand = require('expand-braces');
|
||||
|
||||
var ID_PATTERN = /^[a-f\d]{24}$/;
|
||||
@@ -173,24 +172,18 @@ function configure(app, wares, ctx) {
|
||||
// such as enforcing a type property to exist, are followed.
|
||||
return res.format({
|
||||
text: function() {
|
||||
res.set('Content-Type', 'text/plain');
|
||||
// sgvdata knows how to format sgv entries as text
|
||||
es.pipeline(output, sgvdata.format(), es.writeArray(function(err, out) {
|
||||
res.send(out.join(''));
|
||||
}));
|
||||
res.status(405).send({
|
||||
status: 405,
|
||||
message: 'Method Not Allowed (only json is supported)',
|
||||
type: 'internal'
|
||||
});
|
||||
},
|
||||
csv: function() {
|
||||
// sgvdata knows how to format sgv entries as text
|
||||
res.set('Content-Type', 'text/plain');
|
||||
var csvpipe = require('sgvdata/lib/text')({
|
||||
format: ',',
|
||||
parse: /[\t,]/
|
||||
});
|
||||
es.pipeline(
|
||||
output, sgvdata.mapper(csvpipe.format), es.join('\n'), es.writeArray(function(err, out) {
|
||||
res.send(out.join(''));
|
||||
})
|
||||
);
|
||||
res.status(405).send({
|
||||
status: 405,
|
||||
message: 'Method Not Allowed (only json is supported)',
|
||||
type: 'internal'
|
||||
});
|
||||
},
|
||||
json: function() {
|
||||
// so long as every element has a `type` field, and some kind of
|
||||
@@ -225,32 +218,6 @@ function configure(app, wares, ctx) {
|
||||
incoming = incoming.concat(req.body);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function inputs
|
||||
* @returns {ReadableStream} Readable stream with all incoming elements
|
||||
* in the stream.
|
||||
* in node, pipe is the most interoperable interface
|
||||
* inputs returns a readable stream representing all the potential
|
||||
* records from the HTTP body.
|
||||
* Most content-types are handled by express middeware.
|
||||
* However, text/* types are given to us as a raw buffer, this
|
||||
* function switches between these two variants to find the
|
||||
* correct input stream.
|
||||
* stream, so use svgdata to handle those.
|
||||
* The inputs stream always emits sgv json objects.
|
||||
*/
|
||||
function inputs() {
|
||||
var input;
|
||||
// handle all text types
|
||||
if (req.is('text/*')) {
|
||||
// re-use the svgdata parsing stream
|
||||
input = es.pipeline(req, sgvdata.parse());
|
||||
return input;
|
||||
}
|
||||
// use established list
|
||||
return es.readArray(incoming);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function persist
|
||||
* @returns {WritableStream} a writable persistent storage stream
|
||||
@@ -280,7 +247,7 @@ function configure(app, wares, ctx) {
|
||||
|
||||
// pipe everything to persistent storage
|
||||
// when finished, pass to the next piece of middleware
|
||||
es.pipeline(inputs(), persist(done));
|
||||
es.pipeline(es.readArray(incoming), persist(done));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+591
-8
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var es = require('event-stream');
|
||||
var sgvdata = require('sgvdata');
|
||||
var find_options = require('./query');
|
||||
var ObjectID = require('mongodb').ObjectID;
|
||||
|
||||
@@ -121,7 +120,6 @@ function storage(env, ctx) {
|
||||
|
||||
// Expose all the useful functions
|
||||
api.list = list;
|
||||
api.echo = sgvdata.sync.json.echo;
|
||||
api.map = map;
|
||||
api.create = create;
|
||||
api.remove = remove;
|
||||
|
||||
@@ -1,317 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var es = require('event-stream');
|
||||
var Long = require('long');
|
||||
var decoders = require('sgvdata/lib/protobuf');
|
||||
var direction = require('sgvdata/lib/utils').direction;
|
||||
var moment = require('moment');
|
||||
var url = require('url');
|
||||
|
||||
function init (env, ctx) {
|
||||
|
||||
function mqtt ( ) {
|
||||
return mqtt;
|
||||
}
|
||||
var info = url.parse(env.MQTT_MONITOR);
|
||||
var username = info.auth.split(':').slice(0, -1).join('');
|
||||
var shared_topic = '/downloads/' + username + '/#';
|
||||
var alias_topic = '/downloads/' + username + '/protobuf';
|
||||
var notification_topic = '/notifications/' + username + '/json';
|
||||
env.mqtt_shared_topic = shared_topic;
|
||||
|
||||
mqtt.client = connect(env);
|
||||
var downloads = mqtt.downloads = downloader();
|
||||
|
||||
if (mqtt.client) {
|
||||
listenForMessages(ctx);
|
||||
}
|
||||
|
||||
mqtt.every = every;
|
||||
mqtt.entries = process();
|
||||
|
||||
//expose for tests that don't need to connect
|
||||
mqtt.sgvSensorMerge = sgvSensorMerge;
|
||||
|
||||
function listenForMessages ( ) {
|
||||
mqtt.client.on('message', function (topic, msg) {
|
||||
console.log('topic', topic);
|
||||
// XXX: ugly hack
|
||||
if (topic === alias_topic) {
|
||||
topic = '/downloads/protobuf';
|
||||
}
|
||||
|
||||
console.log(topic, 'on message', 'msg', msg.length);
|
||||
switch (topic) {
|
||||
case '/uploader':
|
||||
console.log({type: topic, msg: msg.toString()});
|
||||
break;
|
||||
case '/downloads/protobuf':
|
||||
downloadProtobuf(msg, topic, downloads, ctx);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log(topic, 'on message', 'msg', msg);
|
||||
// ctx.entries.write(msg);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mqtt.emitNotification = function emitNotification(notify) {
|
||||
console.info('Publishing notification to mqtt: ', notify);
|
||||
[notification_topic, '/notifications/json'].forEach(function iter_notify (topic) {
|
||||
mqtt.client.publish(topic, JSON.stringify(notify), function mqttCallback (err) {
|
||||
if (err) {
|
||||
console.error('Unable to publish notification to MQTT', err);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return mqtt();
|
||||
}
|
||||
|
||||
function connect (env) {
|
||||
var uri = env.MQTT_MONITOR;
|
||||
var shared_topic = env.mqtt_shared_topic;
|
||||
if (!uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var opts = {
|
||||
encoding: 'binary',
|
||||
clean: false,
|
||||
clientId: env.mqtt_client_id
|
||||
};
|
||||
var client = require('mqtt').connect(uri, opts);
|
||||
|
||||
function granted () { console.log('granted', arguments); }
|
||||
|
||||
client.subscribe('sgvs');
|
||||
client.subscribe('published');
|
||||
client.subscribe('/downloads/protobuf', {qos: 2}, granted);
|
||||
client.subscribe(shared_topic, {qos: 2}, granted);
|
||||
client.subscribe('/uploader', granted);
|
||||
client.subscribe('/entries/sgv', granted);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
function process ( ) {
|
||||
var stream = es.through(
|
||||
function _write(data) {
|
||||
this.push(data);
|
||||
}
|
||||
);
|
||||
return stream;
|
||||
}
|
||||
|
||||
function every (storage) {
|
||||
function iter(item, next) {
|
||||
storage.create(item, next);
|
||||
}
|
||||
|
||||
return es.map(iter);
|
||||
}
|
||||
|
||||
function downloader ( ) {
|
||||
var opts = {
|
||||
model: decoders.models.G4Download
|
||||
, json: function (o) {
|
||||
return o;
|
||||
}
|
||||
, payload: function (o) {
|
||||
return o;
|
||||
}
|
||||
};
|
||||
return decoders(opts);
|
||||
}
|
||||
|
||||
function downloadProtobuf (msg, topic, downloads, ctx) {
|
||||
var b = new Buffer(msg, 'binary');
|
||||
console.log('BINARY', b.length, b.toString('hex'));
|
||||
var packet;
|
||||
try {
|
||||
packet = downloads.parse(b);
|
||||
if (!packet.type) {
|
||||
packet.type = topic;
|
||||
|
||||
}
|
||||
console.log('DOWNLOAD msg', msg.length, packet);
|
||||
console.log('download SGV', packet.sgv[0]);
|
||||
console.log('download_timestamp', packet.download_timestamp, new Date(Date.parse(packet.download_timestamp)));
|
||||
console.log('WRITE TO MONGO');
|
||||
var download_timestamp = moment(packet.download_timestamp);
|
||||
if (packet.download_status === 0) {
|
||||
es.readArray(sgvSensorMerge(packet)).pipe(ctx.entries.persist(function empty(err, result) {
|
||||
console.log('DONE WRITING MERGED SGV TO MONGO', err, result);
|
||||
}));
|
||||
|
||||
iter_mqtt_record_stream(packet, 'cal', toCal)
|
||||
.pipe(ctx.entries.persist(function empty(err, result) {
|
||||
console.log('DONE WRITING Cal TO MONGO', err, result.length);
|
||||
}));
|
||||
iter_mqtt_record_stream(packet, 'meter', toMeter)
|
||||
.pipe(ctx.entries.persist(function empty(err, result) {
|
||||
console.log('DONE WRITING Meter TO MONGO', err, result.length);
|
||||
}));
|
||||
}
|
||||
packet.type = 'download';
|
||||
ctx.devicestatus.create({
|
||||
uploaderBattery: packet.uploader_battery,
|
||||
created_at: download_timestamp.toISOString()
|
||||
}, function empty(err, result) {
|
||||
console.log('DONE WRITING TO MONGO devicestatus ', result, err);
|
||||
});
|
||||
|
||||
ctx.entries.create([ packet ], function empty(err) {
|
||||
if (err) {
|
||||
console.log('Error writting to mongo: ', err);
|
||||
} else {
|
||||
console.log('Download written to mongo: ', packet);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.log('DID NOT PARSE', e);
|
||||
}
|
||||
}
|
||||
|
||||
function toSGV (proto, vars) {
|
||||
vars.sgv = proto.sgv_mgdl;
|
||||
vars.direction = direction(proto.trend);
|
||||
vars.noise = proto.noise;
|
||||
vars.type = 'sgv';
|
||||
return vars;
|
||||
}
|
||||
|
||||
function toCal (proto, vars) {
|
||||
vars.slope = proto.slope;
|
||||
vars.intercept = proto.intercept;
|
||||
vars.scale = proto.scale;
|
||||
vars.type = 'cal';
|
||||
return vars;
|
||||
}
|
||||
|
||||
function toSensor (proto, vars) {
|
||||
vars.filtered = new Long(proto.filtered).toInt();
|
||||
vars.unfiltered = new Long(proto.unfiltered).toInt();
|
||||
vars.rssi = proto.rssi;
|
||||
vars.type = 'sensor';
|
||||
return vars;
|
||||
}
|
||||
|
||||
function toMeter (proto, result) {
|
||||
result.type = 'mbg';
|
||||
result.mbg = proto.mbg || proto.meter_bg_mgdl;
|
||||
return result;
|
||||
}
|
||||
|
||||
function toTimestamp (proto, receiver_time, download_time) {
|
||||
var record_offset = receiver_time - proto.sys_timestamp_sec;
|
||||
var record_time = download_time.clone( ).subtract(record_offset, 'second');
|
||||
var obj = {
|
||||
device: 'dexcom'
|
||||
, date: record_time.unix() * 1000
|
||||
, dateString: record_time.format( )
|
||||
};
|
||||
return obj;
|
||||
}
|
||||
|
||||
function timestampFactory (packet) {
|
||||
var receiver_time = packet.receiver_system_time_sec;
|
||||
var download_time = moment(packet.download_timestamp);
|
||||
function timestamp (item) {
|
||||
return toTimestamp(item, receiver_time, download_time.clone( ));
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
function timeSort (a, b) {
|
||||
return a.date - b.date;
|
||||
}
|
||||
|
||||
function sgvSensorMerge (packet) {
|
||||
var timestamp = timestampFactory(packet);
|
||||
var sgvs = (packet['sgv'] || []).map(function(sgv) {
|
||||
var timestamped = timestamp(sgv);
|
||||
return toSGV(sgv, timestamped);
|
||||
}).sort(timeSort);
|
||||
|
||||
var sensors = (packet['sensor'] || []).map(function(sensor) {
|
||||
var timestamped = timestamp(sensor);
|
||||
return toSensor(sensor, timestamped);
|
||||
}).sort(timeSort);
|
||||
|
||||
//based on com.nightscout.core.dexcom.Utils#mergeGlucoseDataRecords
|
||||
var merged = []
|
||||
, sgvsLength = sgvs.length
|
||||
, sensorsLength = sensors.length;
|
||||
|
||||
if (sgvsLength >= 0 && sensorsLength === 0) {
|
||||
merged = sgvs;
|
||||
} else {
|
||||
var smallerLength = Math.min(sgvsLength, sensorsLength);
|
||||
for (var i = 1; i <= smallerLength; i++) {
|
||||
var sgv = sgvs[sgvsLength - i];
|
||||
var sensor = sensors[sensorsLength - i];
|
||||
if (sgv && sensor && Math.abs(sgv.date - sensor.date) < 10000) {
|
||||
//timestamps are close so merge
|
||||
sgv.filtered = sensor.filtered;
|
||||
sgv.unfiltered = sensor.unfiltered;
|
||||
sgv.rssi = sensor.rssi;
|
||||
merged.push(sgv);
|
||||
} else {
|
||||
console.info('mismatch or missing, sgv: ', sgv, ' sensor: ', sensor);
|
||||
//timestamps aren't close enough so add both
|
||||
if (sgv) { merged.push(sgv); }
|
||||
//but the sensor will become and sgv now
|
||||
if (sensor) {
|
||||
sensor.type = 'sgv';
|
||||
merged.push(sensor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//any extra sgvs?
|
||||
if (sgvsLength > smallerLength) {
|
||||
for (var j = 0; j < sgvsLength - smallerLength; j++) {
|
||||
var extraSGV = sgvs[j];
|
||||
merged.push(extraSGV);
|
||||
}
|
||||
}
|
||||
|
||||
//any extra sensors?
|
||||
if (sensorsLength > smallerLength) {
|
||||
for (var k = 0; k < sensorsLength - smallerLength; k++) {
|
||||
var extraSensor = sensors[k];
|
||||
//from now on we consider it a sgv
|
||||
extraSensor.type = 'sgv';
|
||||
merged.push(extraSensor);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
function iter_mqtt_record_stream (packet, prop, sync) {
|
||||
var list = packet[prop];
|
||||
console.log('incoming', prop, (list || [ ]).length);
|
||||
var stream = es.readArray(list || [ ]);
|
||||
var receiver_time = packet.receiver_system_time_sec;
|
||||
var download_time = moment(packet.download_timestamp);
|
||||
function map(item, next) {
|
||||
var timestamped = toTimestamp(item, receiver_time, download_time.clone( ));
|
||||
var r = sync(item, timestamped);
|
||||
if (!('type' in r)) {
|
||||
r.type = prop;
|
||||
}
|
||||
console.log('ITEM', item, 'TO', prop, r);
|
||||
next(null, r);
|
||||
}
|
||||
return stream.pipe(es.map(map));
|
||||
}
|
||||
|
||||
init.downloadProtobuf = downloadProtobuf;
|
||||
module.exports = init;
|
||||
+8
-1
@@ -41,7 +41,10 @@ function init ( ) {
|
||||
, bgTargetTop: 180
|
||||
, bgTargetBottom: 80
|
||||
, bgLow: 55
|
||||
}
|
||||
},
|
||||
insecureUseHttp: false,
|
||||
secureHstsHeader: true,
|
||||
secureCsp: false
|
||||
};
|
||||
|
||||
var valueMappers = {
|
||||
@@ -59,6 +62,10 @@ function init ( ) {
|
||||
, alarmTimeagoUrgent: mapTruthy
|
||||
, alarmWarnMins: mapNumberArray
|
||||
, timeFormat: mapNumber
|
||||
, insecureUseHttp: mapTruthy
|
||||
, secureHstsHeader: mapTruthy
|
||||
, secureCsp: mapTruthy
|
||||
|
||||
};
|
||||
|
||||
function mapNumberArray (value) {
|
||||
|
||||
Generated
+414
-1606
File diff suppressed because it is too large
Load Diff
+13
-15
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "nightscout",
|
||||
"version": "0.11.0-dev-20181022",
|
||||
"version": "0.11.0-dev-20181116",
|
||||
"description": "Nightscout acts as a web-based CGM (Continuous Glucose Montinor) to allow multiple caregivers to remotely view a patients glucose data in realtime.",
|
||||
"license": "AGPL-3.0",
|
||||
"author": "Nightscout Team",
|
||||
@@ -52,17 +52,17 @@
|
||||
"npm": "6.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": "^6.5.4",
|
||||
"ajv": "^6.5.5",
|
||||
"async": "^0.9.2",
|
||||
"body-parser": "^1.18.3",
|
||||
"bootevent": "0.0.1",
|
||||
"compression": "^1.7.3",
|
||||
"css-loader": "^0.28.11",
|
||||
"css-loader": "^1.0.1",
|
||||
"cssmin": "^0.4.3",
|
||||
"d3": "^3.5.17",
|
||||
"ejs": "^2.6.1",
|
||||
"errorhandler": "^1.5.0",
|
||||
"event-stream": "^3.3.6",
|
||||
"event-stream": "^4.0.1",
|
||||
"expand-braces": "^0.1.2",
|
||||
"express": "^4.16.4",
|
||||
"express-minify": "^1.0.0",
|
||||
@@ -72,28 +72,26 @@
|
||||
"jquery-ui-bundle": "^1.12.1-migrate",
|
||||
"jquery.tooltips": "^1.0.0",
|
||||
"js-storage": "^1.0.4",
|
||||
"jsonwebtoken": "^8.3.0",
|
||||
"jsonwebtoken": "^8.4.0",
|
||||
"lodash": "^4.17.11",
|
||||
"long": "^3.2.0",
|
||||
"mime": "^2.3.1",
|
||||
"long": "^4.0.0",
|
||||
"mime": "^2.4.0",
|
||||
"minimed-connect-to-nightscout": "^1.1.1",
|
||||
"moment": "^2.22.2",
|
||||
"moment-timezone": "^0.5.21",
|
||||
"mongodb": "^3.1.8",
|
||||
"moment-timezone": "^0.5.23",
|
||||
"mongodb": "^3.1.10",
|
||||
"mongomock": "^0.1.2",
|
||||
"mqtt": "^2.18.8",
|
||||
"node-cache": "^4.2.0",
|
||||
"parse-duration": "^0.1.1",
|
||||
"prettyjson": "^1.2.1",
|
||||
"pushover-notifications": "^0.2.4",
|
||||
"pushover-notifications": "^1.2.0",
|
||||
"random-token": "0.0.8",
|
||||
"request": "^2.88.0",
|
||||
"sgvdata": "git://github.com/ktind/sgvdata.git#wip/protobuf",
|
||||
"share2nightscout-bridge": "git://github.com/nightscout/share2nightscout-bridge.git#wip/generalize",
|
||||
"shiro-trie": "^0.3.13",
|
||||
"shiro-trie": "^0.4.8",
|
||||
"simple-statistics": "^0.7.0",
|
||||
"socket.io": "^2.1.1",
|
||||
"swagger-ui-dist": "^3.19.3",
|
||||
"swagger-ui-dist": "^3.20.1",
|
||||
"traverse": "^0.6.6",
|
||||
"uglify-js": "^3.4.9",
|
||||
"uuid": "^3.2.1"
|
||||
@@ -109,7 +107,7 @@
|
||||
"should": "^13.2.3",
|
||||
"style-loader": "^0.23.1",
|
||||
"supertest": "^3.3.0",
|
||||
"webpack": "^4.22.0",
|
||||
"webpack": "^4.26.1",
|
||||
"webpack-bundle-analyzer": "^3.0.3",
|
||||
"webpack-cli": "^3.1.2"
|
||||
}
|
||||
|
||||
@@ -54,12 +54,6 @@ require('./lib/server/bootevent')(env, language).boot(function booted (ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (env.MQTT_MONITOR) {
|
||||
ctx.mqtt = require('./lib/server/mqtt')(env, ctx);
|
||||
var es = require('event-stream');
|
||||
es.pipeline(ctx.mqtt.entries, ctx.entries.map( ), ctx.mqtt.every(ctx.entries));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// setup socket io for data and message transmission
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
+6
-6
File diff suppressed because one or more lines are too long
Vendored
+7
-7
@@ -1,11 +1,11 @@
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.eot?25902905');
|
||||
src: url('../font/fontello.eot?25902905#iefix') format('embedded-opentype'),
|
||||
url('../font/fontello.woff2?25902905') format('woff2'),
|
||||
url('../font/fontello.woff?25902905') format('woff'),
|
||||
url('../font/fontello.ttf?25902905') format('truetype'),
|
||||
url('../font/fontello.svg?25902905#fontello') format('svg');
|
||||
src: url('../font/fontello.eot?50735338');
|
||||
src: url('../font/fontello.eot?50735338#iefix') format('embedded-opentype'),
|
||||
url('../font/fontello.woff2?50735338') format('woff2'),
|
||||
url('../font/fontello.woff?50735338') format('woff'),
|
||||
url('../font/fontello.ttf?50735338') format('truetype'),
|
||||
url('../font/fontello.svg?50735338#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.svg?25902905#fontello') format('svg');
|
||||
src: url('../font/fontello.svg?50735338#fontello') format('svg');
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
+30
-33
@@ -229,11 +229,11 @@ body {
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('./font/fontello.eot?94753240');
|
||||
src: url('./font/fontello.eot?94753240#iefix') format('embedded-opentype'),
|
||||
url('./font/fontello.woff?94753240') format('woff'),
|
||||
url('./font/fontello.ttf?94753240') format('truetype'),
|
||||
url('./font/fontello.svg?94753240#fontello') format('svg');
|
||||
src: url('./font/fontello.eot?92660326');
|
||||
src: url('./font/fontello.eot?92660326#iefix') format('embedded-opentype'),
|
||||
url('./font/fontello.woff?92660326') format('woff'),
|
||||
url('./font/fontello.ttf?92660326') format('truetype'),
|
||||
url('./font/fontello.svg?92660326#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@@ -275,7 +275,7 @@ body {
|
||||
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="css/animation.css"><!--[if IE 7]><link rel="stylesheet" href="css/fontello-ie7.css"><![endif]-->
|
||||
<link rel="stylesheet" href="css/animation.css"><!--[if IE 7]><link rel="stylesheet" href="css/" + font.fontname + "-ie7.css"><![endif]-->
|
||||
<script>
|
||||
function toggleCodes(on) {
|
||||
var obj = document.getElementById('icons');
|
||||
@@ -291,49 +291,46 @@ body {
|
||||
</head>
|
||||
<body>
|
||||
<div class="container header">
|
||||
<h1>
|
||||
fontello
|
||||
<small>font demo</small>
|
||||
</h1>
|
||||
<h1>fontello <small>font demo</small></h1>
|
||||
<label class="switch">
|
||||
<input type="checkbox" onclick="toggleCodes(this.checked)">show codes
|
||||
</label>
|
||||
</div>
|
||||
<div id="icons" class="container">
|
||||
<div class="container" id="icons">
|
||||
<div class="row">
|
||||
<div title="Code: 0xe800" class="the-icons span3"><i class="demo-icon icon-help-circled"></i> <span class="i-name">icon-help-circled</span><span class="i-code">0xe800</span></div>
|
||||
<div title="Code: 0xe801" class="the-icons span3"><i class="demo-icon icon-angle-double-up"></i> <span class="i-name">icon-angle-double-up</span><span class="i-code">0xe801</span></div>
|
||||
<div title="Code: 0xe802" class="the-icons span3"><i class="demo-icon icon-angle-double-down"></i> <span class="i-name">icon-angle-double-down</span><span class="i-code">0xe802</span></div>
|
||||
<div title="Code: 0xe803" class="the-icons span3"><i class="demo-icon icon-menu"></i> <span class="i-name">icon-menu</span><span class="i-code">0xe803</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe800"><i class="demo-icon icon-help-circled"></i> <span class="i-name">icon-help-circled</span><span class="i-code">0xe800</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe801"><i class="demo-icon icon-angle-double-up"></i> <span class="i-name">icon-angle-double-up</span><span class="i-code">0xe801</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe802"><i class="demo-icon icon-angle-double-down"></i> <span class="i-name">icon-angle-double-down</span><span class="i-code">0xe802</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe803"><i class="demo-icon icon-menu"></i> <span class="i-name">icon-menu</span><span class="i-code">0xe803</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div title="Code: 0xe804" class="the-icons span3"><i class="demo-icon icon-battery-25"></i> <span class="i-name">icon-battery-25</span><span class="i-code">0xe804</span></div>
|
||||
<div title="Code: 0xe805" class="the-icons span3"><i class="demo-icon icon-battery-50"></i> <span class="i-name">icon-battery-50</span><span class="i-code">0xe805</span></div>
|
||||
<div title="Code: 0xe806" class="the-icons span3"><i class="demo-icon icon-cog"></i> <span class="i-name">icon-cog</span><span class="i-code">0xe806</span></div>
|
||||
<div title="Code: 0xe807" class="the-icons span3"><i class="demo-icon icon-battery-75"></i> <span class="i-name">icon-battery-75</span><span class="i-code">0xe807</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe804"><i class="demo-icon icon-battery-25"></i> <span class="i-name">icon-battery-25</span><span class="i-code">0xe804</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe805"><i class="demo-icon icon-battery-50"></i> <span class="i-name">icon-battery-50</span><span class="i-code">0xe805</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe806"><i class="demo-icon icon-cog"></i> <span class="i-name">icon-cog</span><span class="i-code">0xe806</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe807"><i class="demo-icon icon-battery-75"></i> <span class="i-name">icon-battery-75</span><span class="i-code">0xe807</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div title="Code: 0xe808" class="the-icons span3"><i class="demo-icon icon-battery-100"></i> <span class="i-name">icon-battery-100</span><span class="i-code">0xe808</span></div>
|
||||
<div title="Code: 0xe809" class="the-icons span3"><i class="demo-icon icon-cancel-circled"></i> <span class="i-name">icon-cancel-circled</span><span class="i-code">0xe809</span></div>
|
||||
<div title="Code: 0xe80a" class="the-icons span3"><i class="demo-icon icon-volume"></i> <span class="i-name">icon-volume</span><span class="i-code">0xe80a</span></div>
|
||||
<div title="Code: 0xe80b" class="the-icons span3"><i class="demo-icon icon-plus"></i> <span class="i-name">icon-plus</span><span class="i-code">0xe80b</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe808"><i class="demo-icon icon-battery-100"></i> <span class="i-name">icon-battery-100</span><span class="i-code">0xe808</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe809"><i class="demo-icon icon-cancel-circled"></i> <span class="i-name">icon-cancel-circled</span><span class="i-code">0xe809</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe80a"><i class="demo-icon icon-volume"></i> <span class="i-name">icon-volume</span><span class="i-code">0xe80a</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe80b"><i class="demo-icon icon-plus"></i> <span class="i-name">icon-plus</span><span class="i-code">0xe80b</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div title="Code: 0xe80c" class="the-icons span3"><i class="demo-icon icon-hourglass"></i> <span class="i-name">icon-hourglass</span><span class="i-code">0xe80c</span></div>
|
||||
<div title="Code: 0xe80d" class="the-icons span3"><i class="demo-icon icon-calc"></i> <span class="i-name">icon-calc</span><span class="i-code">0xe80d</span></div>
|
||||
<div title="Code: 0xe80e" class="the-icons span3"><i class="demo-icon icon-tint"></i> <span class="i-name">icon-tint</span><span class="i-code">0xe80e</span></div>
|
||||
<div title="Code: 0xe80f" class="the-icons span3"><i class="demo-icon icon-chart-line"></i> <span class="i-name">icon-chart-line</span><span class="i-code">0xe80f</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe80c"><i class="demo-icon icon-hourglass"></i> <span class="i-name">icon-hourglass</span><span class="i-code">0xe80c</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe80d"><i class="demo-icon icon-calc"></i> <span class="i-name">icon-calc</span><span class="i-code">0xe80d</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe80e"><i class="demo-icon icon-tint"></i> <span class="i-name">icon-tint</span><span class="i-code">0xe80e</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe80f"><i class="demo-icon icon-chart-line"></i> <span class="i-name">icon-chart-line</span><span class="i-code">0xe80f</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div title="Code: 0xe810" class="the-icons span3"><i class="demo-icon icon-sort-numeric"></i> <span class="i-name">icon-sort-numeric</span><span class="i-code">0xe810</span></div>
|
||||
<div title="Code: 0xe811" class="the-icons span3"><i class="demo-icon icon-edit"></i> <span class="i-name">icon-edit</span><span class="i-code">0xe811</span></div>
|
||||
<div title="Code: 0xe812" class="the-icons span3"><i class="demo-icon icon-lock"></i> <span class="i-name">icon-lock</span><span class="i-code">0xe812</span></div>
|
||||
<div title="Code: 0xe813" class="the-icons span3"><i class="demo-icon icon-lock-open"></i> <span class="i-name">icon-lock-open</span><span class="i-code">0xe813</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe810"><i class="demo-icon icon-sort-numeric"></i> <span class="i-name">icon-sort-numeric</span><span class="i-code">0xe810</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe811"><i class="demo-icon icon-edit"></i> <span class="i-name">icon-edit</span><span class="i-code">0xe811</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe812"><i class="demo-icon icon-lock"></i> <span class="i-name">icon-lock</span><span class="i-code">0xe812</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe813"><i class="demo-icon icon-lock-open"></i> <span class="i-name">icon-lock-open</span><span class="i-code">0xe813</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div title="Code: 0xe814" class="the-icons span3"><i class="demo-icon icon-trash-empty"></i> <span class="i-name">icon-trash-empty</span><span class="i-code">0xe814</span></div>
|
||||
<div class="the-icons span3" title="Code: 0xe814"><i class="demo-icon icon-trash-empty"></i> <span class="i-name">icon-trash-empty</span><span class="i-code">0xe814</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container footer">Generated by <a href="http://fontello.com">fontello.com</a></div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Copyright (C) 2016 by original authors @ fontello.com</metadata>
|
||||
<metadata>Copyright (C) 2018 by original authors @ fontello.com</metadata>
|
||||
<defs>
|
||||
<font id="fontello" horiz-adv-x="1000" >
|
||||
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
|
||||
|
||||
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,182 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var should = require('should');
|
||||
|
||||
var FIVE_MINS = 5 * 60 * 1000;
|
||||
|
||||
describe('mqtt', function ( ) {
|
||||
|
||||
var self = this;
|
||||
|
||||
before(function () {
|
||||
process.env.MQTT_MONITOR = 'mqtt://user:password@localhost:12345';
|
||||
process.env.STORAGE_URI='mongodb://localhost:27017/test_db';
|
||||
process.env.ENTRIES_COLLECTION='test_sgvs';
|
||||
self.env = require('../env')();
|
||||
self.es = require('event-stream');
|
||||
self.results = self.es.through(function (ch) { this.push(ch); });
|
||||
function outputs (fn) {
|
||||
return self.es.writeArray(function (err, results) {
|
||||
fn(err, results);
|
||||
self.results.write(err || results);
|
||||
});
|
||||
}
|
||||
function written (data, fn) {
|
||||
self.results.write(data);
|
||||
setTimeout(fn, 5);
|
||||
}
|
||||
self.mqtt = require('../lib/server/mqtt')(self.env, {entries: { persist: outputs, create: written }, devicestatus: { create: written } });
|
||||
});
|
||||
|
||||
after(function () {
|
||||
delete process.env.MQTT_MONITOR;
|
||||
});
|
||||
|
||||
var now = Date.now()
|
||||
, prev1 = now - FIVE_MINS
|
||||
, prev2 = prev1 - FIVE_MINS
|
||||
;
|
||||
|
||||
it('setup env correctly', function (done) {
|
||||
self.env.mqtt_client_id.should.equal('nGVkio2g7p9+WOoiHB9YgmM');
|
||||
done();
|
||||
});
|
||||
|
||||
it('handle a download with only sgvs', function (done) {
|
||||
var packet = {
|
||||
sgv: [
|
||||
{sgv_mgdl: 110, trend: 4, date: prev2}
|
||||
, {sgv_mgdl: 105, trend: 4, date: prev1}
|
||||
, {sgv_mgdl: 100, trend: 4, date: now}
|
||||
]
|
||||
};
|
||||
|
||||
var merged = self.mqtt.sgvSensorMerge(packet);
|
||||
|
||||
merged.length.should.equal(packet.sgv.length);
|
||||
|
||||
done();
|
||||
|
||||
});
|
||||
|
||||
it('merge sgvs and sensor records that match up', function (done) {
|
||||
var packet = {
|
||||
sgv: [
|
||||
{sgv_mgdl: 110, trend: 4, date: prev2}
|
||||
, {sgv_mgdl: 105, trend: 4, date: prev1}
|
||||
, {sgv_mgdl: 100, trend: 4, date: now}
|
||||
]
|
||||
, sensor: [
|
||||
{filtered: 99999, unfiltered: 99999, rssi: 200, date: prev2}
|
||||
, {filtered: 99999, unfiltered: 99999, rssi: 200, date: prev1}
|
||||
, {filtered: 99999, unfiltered: 99999, rssi: 200, date: now}
|
||||
]
|
||||
};
|
||||
|
||||
var merged = self.mqtt.sgvSensorMerge(packet);
|
||||
|
||||
merged.length.should.equal(packet.sgv.length);
|
||||
|
||||
merged.filter(function (sgv) {
|
||||
return sgv.filtered && sgv.unfiltered && sgv.rssi;
|
||||
}).length.should.equal(packet.sgv.length);
|
||||
|
||||
done();
|
||||
|
||||
});
|
||||
|
||||
it('downloadProtobuf should dispatch', function (done) {
|
||||
|
||||
var payload = new Buffer('0a1108b70110d6d1fa6318f08df963200428011a1d323031352d30382d32335432323a35333a35352e3634392d30373a303020d7d1fa6328004a1508e0920b10c0850b18b20120d5d1fa6328ef8df963620a534d34313837393135306a053638393250', 'hex');
|
||||
|
||||
// var payload = self.mqtt.downloads.format(packet);
|
||||
console.log('yaploda', '/downloads/protobuf', payload);
|
||||
var l = [ ];
|
||||
self.results.on('data', function (chunk) {
|
||||
l.push(chunk);
|
||||
console.log('test data', l.length, chunk.length, chunk);
|
||||
switch (l.length) {
|
||||
case 0: // devicestatus
|
||||
break;
|
||||
case 2: // sgv
|
||||
break;
|
||||
case 3: // sgv
|
||||
chunk.length.should.equal(1);
|
||||
var first = chunk[0];
|
||||
should.exist(first.sgv);
|
||||
should.exist(first.noise);
|
||||
should.exist(first.date);
|
||||
should.exist(first.dateString);
|
||||
first.type.should.equal('sgv');
|
||||
break;
|
||||
case 4: // cal
|
||||
break;
|
||||
case 1: // meter
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (l.length >= 5) {
|
||||
self.results.end( );
|
||||
}
|
||||
});
|
||||
self.results.on('end', function ( ) {
|
||||
done( );
|
||||
});
|
||||
self.mqtt.client.emit('message', '/downloads/protobuf', payload);
|
||||
});
|
||||
|
||||
it('merge sgvs and sensor records that match up, and get the sgvs that don\'t match', function (done) {
|
||||
var packet = {
|
||||
sgv: [
|
||||
{sgv_mgdl: 110, trend: 4, date: prev2}
|
||||
, {sgv_mgdl: 105, trend: 4, date: prev1}
|
||||
, {sgv_mgdl: 100, trend: 4, date: now}
|
||||
]
|
||||
, sensor: [
|
||||
{filtered: 99999, unfiltered: 99999, rssi: 200, date: now}
|
||||
]
|
||||
};
|
||||
|
||||
var merged = self.mqtt.sgvSensorMerge(packet);
|
||||
|
||||
merged.length.should.equal(packet.sgv.length);
|
||||
|
||||
var withBoth = merged.filter(function (sgv) {
|
||||
return sgv.sgv && sgv.filtered && sgv.unfiltered && sgv.rssi;
|
||||
});
|
||||
|
||||
withBoth.length.should.equal(1);
|
||||
|
||||
done();
|
||||
|
||||
});
|
||||
|
||||
it('merge sgvs and sensor records that match up, and get the sensors that don\'t match', function (done) {
|
||||
var packet = {
|
||||
sgv: [
|
||||
{sgv_mgdl: 100, trend: 4, date: now}
|
||||
]
|
||||
, sensor: [
|
||||
{filtered: 99999, unfiltered: 99999, rssi: 200, date: prev2}
|
||||
, {filtered: 99999, unfiltered: 99999, rssi: 200, date: prev1}
|
||||
, {filtered: 99999, unfiltered: 99999, rssi: 200, date: now}
|
||||
]
|
||||
};
|
||||
|
||||
var merged = self.mqtt.sgvSensorMerge(packet);
|
||||
|
||||
merged.length.should.equal(packet.sensor.length);
|
||||
|
||||
var withBoth = merged.filter(function (sgv) {
|
||||
return sgv.sgv && sgv.filtered && sgv.unfiltered && sgv.rssi;
|
||||
});
|
||||
|
||||
withBoth.length.should.equal(1);
|
||||
|
||||
done();
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
@@ -29,6 +29,9 @@ describe('settings', function ( ) {
|
||||
settings.alarmTimeagoUrgentMins.should.equal(30);
|
||||
settings.language.should.equal('en');
|
||||
settings.showPlugins.should.equal('');
|
||||
settings.insecureUseHttp.should.equal(false);
|
||||
settings.secureHstsHeader.should.equal(true);
|
||||
settings.secureCsp.should.equal(false);
|
||||
});
|
||||
|
||||
it('support setting from env vars', function () {
|
||||
|
||||
Reference in New Issue
Block a user