Skip to content

Commit

Permalink
Merge pull request tidepool-org#559 from tidepool-org/dorianscholz/fs…
Browse files Browse the repository at this point in the history
…librepro

Support for Abbott FreeStyle Libre Pro
  • Loading branch information
gniezen authored Feb 15, 2018
2 parents 56cf1f7 + eb36b57 commit 33c6d40
Show file tree
Hide file tree
Showing 9 changed files with 809 additions and 806 deletions.
2 changes: 1 addition & 1 deletion app/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "tidepool-uploader",
"productName": "tidepool-uploader",
"version": "2.2.5",
"version": "2.3.0",
"description": "Tidepool Project Universal Uploader",
"main": "./main.js",
"author": {
Expand Down
46 changes: 26 additions & 20 deletions app/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ ansi-regex@^2.0.0:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"

aproba@^1.0.3:
version "1.1.2"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1"
version "1.2.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"

are-we-there-yet@~1.1.2:
version "1.1.4"
Expand Down Expand Up @@ -114,8 +114,8 @@ combined-stream@^1.0.5, combined-stream@~1.0.5:
delayed-stream "~1.0.0"

commander@^2.11.0:
version "2.11.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
version "2.12.2"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.12.2.tgz#0f5946c427ed9ec0d91a46bb9def53e54650e555"

[email protected]:
version "0.0.1"
Expand Down Expand Up @@ -160,8 +160,8 @@ delegates@^1.0.0:
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"

detect-libc@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.2.tgz#71ad5d204bf17a6a6ca8f450c61454066ef461e1"
version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"

ecc-jsbn@~0.1.1:
version "0.1.1"
Expand All @@ -183,10 +183,14 @@ extend@~3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"

[email protected], extsprintf@^1.2.0:
[email protected]:
version "1.3.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"

extsprintf@^1.2.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"

forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
Expand Down Expand Up @@ -306,8 +310,8 @@ inherits@2, inherits@~2.0.0, inherits@~2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"

ini@~1.3.0:
version "1.3.4"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"

is-fullwidth-code-point@^1.0.0:
version "1.0.0"
Expand Down Expand Up @@ -403,12 +407,14 @@ [email protected]:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2"

nan@^2.6.2:
version "2.7.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46"
version "2.8.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a"

node-abi@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.1.1.tgz#c9cda256ec8aa99bcab2f6446db38af143338b2a"
version "2.1.2"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.1.2.tgz#4da6caceb6685fcd31e7dd1994ef6bb7d0a9c0b2"
dependencies:
semver "^5.4.1"

[email protected]:
version "0.5.7"
Expand Down Expand Up @@ -496,8 +502,8 @@ performance-now@^0.2.0:
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"

prebuild-install@^2.2.2:
version "2.3.0"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-2.3.0.tgz#19481247df728b854ab57b187ce234211311b485"
version "2.4.1"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-2.4.1.tgz#c28ba1d1eedc17fbd6b3229a657ffc0fba479b49"
dependencies:
expand-template "^1.0.2"
github-from-package "0.0.0"
Expand All @@ -523,8 +529,8 @@ promirepl@^1.0.1:
resolved "https://registry.yarnpkg.com/promirepl/-/promirepl-1.0.1.tgz#2951aaeba2bf3fe2274ff63a16d94c04ca60872e"

pump@^1.0.0, pump@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.2.tgz#3b3ee6512f94f0e575538c17995f9f16990a5d51"
version "1.0.3"
resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.3.tgz#5dfe8311c33bbf6fc18261f9f34702c47c08a954"
dependencies:
end-of-stream "^1.1.0"
once "^1.3.1"
Expand Down Expand Up @@ -595,7 +601,7 @@ safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"

semver@^5.3.0:
semver@^5.3.0, semver@^5.4.1:
version "5.4.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"

Expand Down Expand Up @@ -698,8 +704,8 @@ tar-pack@^3.4.0:
uid-number "^0.0.6"

tar-stream@^1.1.2:
version "1.5.4"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.4.tgz#36549cf04ed1aee9b2a30c0143252238daf94016"
version "1.5.5"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.5.tgz#5cad84779f45c83b1f2508d96b09d88c7218af55"
dependencies:
bl "^1.0.0"
end-of-stream "^1.0.0"
Expand Down
3 changes: 2 additions & 1 deletion lib/core/device.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ device._driverManifests = {
'AbbottFreeStyleLibre': {
mode: 'HID',
usb: [
{vendorId: 6753, productId: 13904}
{vendorId: 6753, productId: 13904}, // FreeStyle Libre
{vendorId: 6753, productId: 13936} // FreeStyle Libre Pro
]
},
'BayerContourNext': {
Expand Down
114 changes: 88 additions & 26 deletions lib/drivers/abbott/abbottFreeStyleLibre.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,21 @@
* == BSD2 LICENSE ==
*/

import { clone, assign } from 'lodash';
import { clone, assign, forEach, forEachRight, map } from 'lodash';
import async from 'async';
import sundial from 'sundial';
import crypto from 'crypto';

import FreeStyleLibreProtocol from './freeStyleLibreProtocol';
import FreeStyleLibreData from './freeStyleLibreData';
import { DB_TABLE_ID, CFG_TABLE_ID, DEVICE_MODEL_NAME, COMPRESSION } from './freeStyleLibreConstants';
import {
FSLIBRE_PRO_PRODUCT_ID,
DB_TABLE_ID,
CFG_TABLE_ID,
DEVICE_MODEL_NAME,
COMPRESSION,
OP_CODE,
} from './freeStyleLibreConstants';

const isBrowser = typeof window !== 'undefined';
// eslint-disable-next-line no-console
Expand Down Expand Up @@ -77,7 +85,7 @@ export default function (config) {
return cb(err, null);
}
data.connect = true;
result.forEach((element) => {
forEach(result, (element) => {
if (typeof element === 'object') {
debug('getConfigInfo: result object: ', element);
assign(data.deviceInfo, element);
Expand All @@ -93,37 +101,64 @@ export default function (config) {
fetchData(progress, data, cb) {
progress(0);

const getterFunctions = [
(callback) => { protocol.setCompression(COMPRESSION.ENABLED, callback); },
(callback) => { protocol.getDbSchema(callback); },
(callback) => { protocol.getCfgSchema(callback); },
(callback) => { protocol.getDateTime(callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.METER_FACTORY_CONFIGURATION, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.METER_SETTINGS, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.USER_PATIENT_CONFIGURATION, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.USER_PATIENT_SETTINGS, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.INSULIN_SETTINGS, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.REMINDER_STRING, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.SMART_TAG_NOTES, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.STORED_SENSOR_INFORMATION, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.REMINDER_DATA, callback); },
(callback) => { protocol.getDatabase(DB_TABLE_ID.GLUCOSE_RESULT, callback); },
(callback) => { protocol.getDatabase(DB_TABLE_ID.RAPID_ACTING_INSULIN, callback); },
(callback) => { protocol.getDatabase(DB_TABLE_ID.HISTORICAL_DATA, callback); },
(callback) => { protocol.getDatabase(DB_TABLE_ID.EVENT, callback); },
];
let getterFunctions;
if (data.deviceInfo.productId === FSLIBRE_PRO_PRODUCT_ID) {
getterFunctions = [
(callback) => { protocol.getDbSchema(callback); },
(callback) => { protocol.getCfgSchema(callback); },
(callback) => { protocol.getDateTime(callback); },
(callback) => {
protocol.getCfgData(CFG_TABLE_ID.METER_FACTORY_CONFIGURATION, callback);
},
(callback) => { protocol.getCfgData(CFG_TABLE_ID.METER_SETTINGS, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.USER_PATIENT_CONFIGURATION, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.USER_PATIENT_SETTINGS, callback); },
(callback) => { protocol.getDatabase(DB_TABLE_ID.GLUCOSE_RESULT, callback); },
(callback) => { protocol.getDatabase(DB_TABLE_ID.HISTORICAL_DATA, callback); },
(callback) => { protocol.getDatabase(DB_TABLE_ID.EVENT, callback); },
];
} else {
getterFunctions = [
(callback) => { protocol.setCompression(COMPRESSION.ENABLED, callback); },
(callback) => { protocol.getDbSchema(callback); },
(callback) => { protocol.getCfgSchema(callback); },
(callback) => { protocol.getDateTime(callback); },
(callback) => {
protocol.getCfgData(CFG_TABLE_ID.METER_FACTORY_CONFIGURATION, callback);
},
(callback) => { protocol.getCfgData(CFG_TABLE_ID.METER_SETTINGS, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.USER_PATIENT_CONFIGURATION, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.USER_PATIENT_SETTINGS, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.INSULIN_SETTINGS, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.REMINDER_STRING, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.SMART_TAG_NOTES, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.STORED_SENSOR_INFORMATION, callback); },
(callback) => { protocol.getCfgData(CFG_TABLE_ID.REMINDER_DATA, callback); },
(callback) => { protocol.getDatabase(DB_TABLE_ID.GLUCOSE_RESULT, callback); },
(callback) => { protocol.getDatabase(DB_TABLE_ID.RAPID_ACTING_INSULIN, callback); },
(callback) => { protocol.getDatabase(DB_TABLE_ID.HISTORICAL_DATA, callback); },
(callback) => { protocol.getDatabase(DB_TABLE_ID.EVENT, callback); },
];
}

data.aapPackets = [];
let counter = 0;
async.series(getterFunctions, (err, results) => {
const updateProgress = (getterFunction, callback) => {
getterFunction(callback);
counter += 1;
progress(100 * (counter / getterFunctions.length));
};

// apply updateProgress function to all getterFunctions to update progress after each one
getterFunctions = map(getterFunctions, getterFunction =>
(callback => updateProgress(getterFunction, callback)));

data.aapPackets = [];
async.series(getterFunctions, (err, results) => {
if (err) {
debug('fetchData: error: ', err);
return cb(err, data);
}
results.forEach((aapPackets) => {
forEach(results, (aapPackets) => {
if (typeof aapPackets === 'object') {
data.aapPackets = data.aapPackets.concat(aapPackets);
}
Expand All @@ -136,7 +171,34 @@ export default function (config) {
debug('processData: num aapPackets:', data.aapPackets.length);
progress(0);

data.deviceInfo.deviceId = `${data.deviceInfo.driverId}-${data.deviceInfo.serialNumber}`;
if (data.deviceInfo.productId === FSLIBRE_PRO_PRODUCT_ID) {
// The unique ID of the sensor read with the Libre Pro handheld device cannot be read from
// the device according to the available documentation.
// To still be able to identify each sensor, a unique ID is generated by hashing the oldest
// historical glucose data packet.
// This packet contains a timestamp and the handheld device's DB record number which make it
// unique for this device and globally unique in combination with the device ID.
let generatedSensorId = '';
const TABLE_ID_OFFSET = 0;
// find oldest historical data packet
forEachRight(data.aapPackets, (aapPacket) => {
if (aapPacket.opCode === OP_CODE.GET_DATABASE &&
aapPacket.data[TABLE_ID_OFFSET] === DB_TABLE_ID.HISTORICAL_DATA) {
// generate unique ID by hashing oldest historical data packet to identify this sensor
const hash = crypto.createHash('sha1');
hash.update(aapPacket.data);
generatedSensorId = hash.digest('hex');
}
});

// append generatedSensorId to deviceId to be able to identify duplicate uploads of the
// same sensor to different patient accounts
data.deviceInfo.deviceId = `${data.deviceInfo.driverId}Pro-${data.deviceInfo.serialNumber}-${generatedSensorId}`;

debug('processData: Libre Pro device/sensor ID:', data.deviceInfo.deviceId);
} else {
data.deviceInfo.deviceId = `${data.deviceInfo.driverId}-${data.deviceInfo.serialNumber}`;
}
cfg.builder.setDefaults({ deviceId: data.deviceInfo.deviceId });

data.post_records =
Expand Down
1 change: 1 addition & 0 deletions lib/drivers/abbott/freeStyleLibreConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

export const DEVICE_MODEL_NAME = 'FreeStyle Libre';
export const FSLIBRE_PRO_PRODUCT_ID = 0x3670;

export const KETONE_VALUE_FACTOR = 18.0; // according to specs
export const KETONE_HI = 8.0;
Expand Down
7 changes: 4 additions & 3 deletions lib/drivers/abbott/freeStyleLibreData.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,11 @@ export default class FreeStyleLibreData {

// the oldest record number in the result DB is used as limit how far back the history records
// are processed but in case the result record DB does not contain any data, process at least
// the last 1000 records this number is lower than the maximal size of the result DB, so we will
// not process further back than any potentially truncated time change records
// the last 5000 records
// this number is lower than the maximal size of the result DB, so we will not process further
// back than any potentially truncated time change records
const oldestValidRecordNumber =
Math.max(0, this.oldestResultRecordNumber - Math.max(0, 1000 - this.numResultRecords));
Math.max(0, this.oldestResultRecordNumber - Math.max(0, 5000 - this.numResultRecords));

// use only records that are newer than the oldestValidRecordNumber
// older records cannot be properly timestamped due to potentially truncated time change records
Expand Down
5 changes: 4 additions & 1 deletion lib/drivers/abbott/tools/fslibre_usb_dissector.lua
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,10 @@ function fslibre_usb.init()
pkt_state = {}
partial_aap_buf = {}

-- register this disector for USB vendor:product 1a61:3650
-- register this disector for USB vendor:product 1a61:3650 (FreeStyle Libre)
DissectorTable.get("usb.product"):add(0x1a613650, fslibre_usb)

-- register this disector for USB vendor:product 1a61:3670 (FreeStyle Libre Pro)
DissectorTable.get("usb.product"):add(0x1a613670, fslibre_usb)
end

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tidepool-uploader",
"version": "2.2.5",
"version": "2.3.0",
"description": "Tidepool Project Universal Uploader",
"private": true,
"main": "main.js",
Expand Down Expand Up @@ -198,7 +198,7 @@
"eslint-import-resolver-webpack": "0.8.3",
"eslint-plugin-import": "2.8.0",
"eslint-plugin-jsx-a11y": "6.0.2",
"eslint-plugin-lodash": "2.4.3",
"eslint-plugin-lodash": "2.5.0",
"eslint-plugin-mocha": "4.11.0",
"eslint-plugin-promise": "3.6.0",
"eslint-plugin-react": "7.4.0",
Expand All @@ -216,6 +216,7 @@
"less-loader": "4.0.5",
"minimist": "1.2.0",
"nodegit": "0.20.3",
"node-hid": "0.5.7",
"object-invariant-test-helper": "0.1.1",
"open": "0.0.5",
"react-hot-loader": "3.1.3",
Expand Down
Loading

0 comments on commit 33c6d40

Please sign in to comment.