Merge branch 'dev' into 181121-minorLgGER

This commit is contained in:
Lukas Herzog
2018-11-28 23:47:08 +01:00
committed by GitHub
22 changed files with 1137 additions and 2251 deletions
+8 -3
View File
@@ -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.
+20 -6
View File
@@ -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/']
}
}));
}
}
}
+25
View File
@@ -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": [
-14
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -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;
-317
View File
@@ -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
View File
@@ -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) {
+414 -1606
View File
File diff suppressed because it is too large Load Diff
+13 -15
View File
@@ -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"
}
-6
View File
@@ -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
///////////////////////////////////////////////////
File diff suppressed because one or more lines are too long
+7 -7
View File
@@ -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
View File
@@ -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">&#xe800;</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">&#xe801;</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">&#xe802;</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">&#xe803;</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">&#xe800;</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">&#xe801;</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">&#xe802;</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">&#xe803;</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">&#xe804;</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">&#xe805;</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">&#xe806;</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">&#xe807;</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">&#xe804;</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">&#xe805;</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">&#xe806;</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">&#xe807;</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">&#xe808;</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">&#xe809;</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">&#xe80a;</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">&#xe80b;</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">&#xe808;</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">&#xe809;</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">&#xe80a;</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">&#xe80b;</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">&#xe80c;</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">&#xe80d;</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">&#xe80e;</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">&#xe80f;</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">&#xe80c;</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">&#xe80d;</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">&#xe80e;</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">&#xe80f;</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">&#xe810;</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">&#xe811;</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">&#xe812;</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">&#xe813;</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">&#xe810;</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">&#xe811;</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">&#xe812;</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">&#xe813;</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">&#xe814;</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">&#xe814;</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 -1
View File
@@ -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.
-182
View File
@@ -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();
});
});
+3
View File
@@ -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 () {