From a6e22f03b65dcdd42230d844108c8a2574407da3 Mon Sep 17 00:00:00 2001 From: Mark Wilson Date: Tue, 6 Oct 2015 14:47:56 -0700 Subject: [PATCH] Gather CGM data! --- config.js.example | 3 +- scraper.js | 80 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/config.js.example b/config.js.example index 464f6b2..ca44467 100644 --- a/config.js.example +++ b/config.js.example @@ -1,7 +1,8 @@ exports.CARELINK_USERNAME = 'username'; exports.CARELINK_PASSWORD = 'password'; exports.PUMP_TIMEZONE = '-0700'; // UTC time offset -exports.CARELINK_REQUEST_INTERVAL = 60 * 1000; // ms +exports.CARELINK_REQUEST_INTERVAL = 60 * 1000; // milliseconds +exports.NUM_RECORDS_TO_SUBMIT = 20; // carelink gives ~300 sgv records at a time, but there's no need to send them all to nightscout exports.NIGHTSCOUT_HOST = 'https://yoursitename.azurewebsites.net'; exports.NIGHTSCOUT_API_SECRET = 'your api secret'; diff --git a/scraper.js b/scraper.js index 88f46c1..ca7b6d9 100644 --- a/scraper.js +++ b/scraper.js @@ -18,32 +18,78 @@ var isNewData = (function() { }; })(); +function addTimeToEntry(pumpTimeString, entry) { + var timeUTC = Date.parse(pumpTimeString + ' ' + config.PUMP_TIMEZONE); + entry['date'] = timeUTC; + entry['dateString'] = new Date(timeUTC).toISOString(); + return entry; +} + +function transformForNightscout(data) { + var entries = []; + + if(data['activeInsulin']) { + entries.push( + addTimeToEntry( + data['activeInsulin']['datetime'], + { + 'type': 'reported_active_insulin', + 'activeInsulin': data['activeInsulin']['amount'] + } + ) + ); + } + + if(data['sgs'] && data['sgs'].length) { + var sgvs = data['sgs'].filter(function(entry) { + return entry['kind'] === 'SG' && entry['sg'] !== 0; + }); + // TODO: don't assume minimed will continue giving sensor glucose values ordered by date ascending + for(var i = Math.max(0, sgvs.length - config.NUM_RECORDS_TO_SUBMIT); i < sgvs.length; i++) { + var sgv = sgvs[i]; + entries.push( + addTimeToEntry( + sgv['datetime'], + { + 'type': 'sgv', + 'sgv': sgv['sg'], + } + ) + ); + } + } + + entries.forEach(function(entry) { + entry['device'] = 'MiniMed Connect ' + data['medicalDeviceFamily'] + ' ' + data['medicalDeviceSerialNumber']; + }); + + var activeEntry = entries.filter(function(e) { return e['type'] === 'reported_active_insulin'; })[0]; + var activeIns = activeEntry ? activeEntry['activeInsulin'] + ' at ' + activeEntry['dateString'] : 'unknown'; + var sgvEntries = entries.filter(function(e) { return e['type'] === 'sgv'; }); + var recentSgv = sgvEntries.length ? sgvEntries[sgvEntries.length - 1] + ' at ' + activeEntry['dateString'] : 'unknown'; + casper.log('active insulin ' + activeIns, 'info'); + casper.log('sensor glucose ' + recentSgv, 'info'); + + return entries; +} + function sendToNightscout(data, callback) { - var activeIns, timeReportedUTC; - try { - activeIns = data['activeInsulin']['amount']; - timeReportedUTC = Date.parse(data['activeInsulin']['datetime'] + ' ' + config.PUMP_TIMEZONE); - } catch(e) { - casper.log('Error parsing JSON data: ' + e.message + ' ' + JSON.stringify(data), 'error'); + var endpoint = config.NIGHTSCOUT_HOST + '/api/v1/entries.json'; + var entries = transformForNightscout(data); + if(entries.length === 0) { + casper.log('No data found in CareLink JSON: ' + JSON.stringify(data), 'error'); return callback.apply(casper); } - casper.log('SENDING TO NIGHTSCOUT: active insulin ' + activeIns + ', reported at ' + new Date(timeReportedUTC).toString(), 'info'); - - casper.open(config.NIGHTSCOUT_HOST + '/api/v1/entries.json', { + casper.log('POST ' + endpoint + ' ' + JSON.stringify(entries), 'info'); + casper.open(endpoint, { method: 'post', + data: JSON.stringify(entries), headers: { 'api-secret': new Rusha().digest(config.NIGHTSCOUT_API_SECRET), 'Accept': 'application/json', 'Content-Type': 'application/json' - }, - data: JSON.stringify({ - 'type': 'reported_active_insulin', - 'device': 'MiniMed Connect ' + data['medicalDeviceFamily'] + ' ' + data['medicalDeviceSerialNumber'], - 'date': timeReportedUTC, - 'dateString': new Date(timeReportedUTC).toISOString(), - 'activeInsulin': activeIns - }) + } }).then(function(response) { if(!response.status) { casper.log("Error uploading to Nightscout: can't connect to Nightscout host", 'error');