From 3db69b36edd25b43cb0cc0ddc6f78d1d161ddf0d Mon Sep 17 00:00:00 2001 From: Lennart Goedhart Date: Tue, 29 Aug 2017 08:50:43 +1200 Subject: [PATCH] Revert "Support for FreeStyle Libre" --- .babelrc | 9 +- .eslintignore | 1 - .gitignore | 2 - app/reducers/devices.js | 8 - docs/checklists/README.md | 5 +- docs/checklists/abbottFreeStyleLibre.md | 147 ---- docs/checklisttemplates/CGMChecklist.md | 4 + lib/core/device.js | 3 - lib/drivers/abbott/.eslintrc | 61 -- lib/drivers/abbott/abbottFreeStyleLibre.js | 207 ------ lib/drivers/abbott/cli/fslibre.js | 168 ----- lib/drivers/abbott/cli/stringify.js | 81 --- lib/drivers/abbott/freeStyleLibreConstants.js | 487 ------------- lib/drivers/abbott/freeStyleLibreData.js | 466 ------------ lib/drivers/abbott/freeStyleLibreProtocol.js | 667 ------------------ lib/drivers/abbott/tools/README | 4 - .../abbott/tools/fslibre_usb_dissector.lua | 278 -------- lib/drivers/abbott/tools/lua_debug.sh | 33 - lib/struct.js | 14 +- manifest.json | 7 - package.json | 19 +- test/lib/abbot/testFreeStyleLibreData.js | 130 ---- test/lib/abbot/testFreeStyleLibreProtocol.js | 133 ---- test/lib/testStruct.js | 26 +- yarn.lock | 537 +++++++------- 25 files changed, 278 insertions(+), 3219 deletions(-) delete mode 100644 docs/checklists/abbottFreeStyleLibre.md delete mode 100644 lib/drivers/abbott/.eslintrc delete mode 100644 lib/drivers/abbott/abbottFreeStyleLibre.js delete mode 100755 lib/drivers/abbott/cli/fslibre.js delete mode 100644 lib/drivers/abbott/cli/stringify.js delete mode 100644 lib/drivers/abbott/freeStyleLibreConstants.js delete mode 100644 lib/drivers/abbott/freeStyleLibreData.js delete mode 100644 lib/drivers/abbott/freeStyleLibreProtocol.js delete mode 100644 lib/drivers/abbott/tools/README delete mode 100644 lib/drivers/abbott/tools/fslibre_usb_dissector.lua delete mode 100755 lib/drivers/abbott/tools/lua_debug.sh delete mode 100644 test/lib/abbot/testFreeStyleLibreData.js delete mode 100644 test/lib/abbot/testFreeStyleLibreProtocol.js diff --git a/.babelrc b/.babelrc index fd37f72076..c31d5a0af4 100644 --- a/.babelrc +++ b/.babelrc @@ -4,7 +4,6 @@ "stage-0", "react" ], - "retainLines": true, "plugins": ["add-module-exports"], "env": { "production": { @@ -20,13 +19,7 @@ }, "test": { "plugins": [ - [ "module-resolver", { - "root": ["./app/node_modules"], - "alias": { - "node-hid": "./app/node_modules/node-hid", - "serialport": "./app/node_modules/serialport" - } - } ], + ["resolver", { "resolveDirs": [ "app/node_modules" ]}], ["webpack-loaders", { "config": "webpack.config.test.js", "verbose": false }], "babel-plugin-rewire", ["transform-define", { diff --git a/.eslintignore b/.eslintignore index c44e35bdc7..5c81f84c22 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,3 @@ app/dist/ app/main.js node_modules -dist/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index bbb7eb2362..fb430899a8 100644 --- a/.gitignore +++ b/.gitignore @@ -48,5 +48,3 @@ coverage _book/ web/ - -\.vscode/ diff --git a/app/reducers/devices.js b/app/reducers/devices.js index e4fc099497..3392982227 100644 --- a/app/reducers/devices.js +++ b/app/reducers/devices.js @@ -66,14 +66,6 @@ const devices = { source: {type: 'device', driverId: 'AbbottFreeStyleFreedomLite'}, enabled: {mac: false, win: true} }, - abbottfreestylelibre: { - instructions: 'Plug in meter with micro-USB', - key: 'abbottfreestylelibre', - name: 'Abbott FreeStyle Libre', - showDriverLink: {linux: false, mac: false, win: false}, - source: {type: 'device', driverId: 'AbbottFreeStyleLibre'}, - enabled: {linux: true, mac: true, win: true} - }, bayercontournext: { instructions: 'Plug in meter with micro-USB', key: 'bayercontournext', diff --git a/docs/checklists/README.md b/docs/checklists/README.md index d3d208734f..e863f5a1fb 100644 --- a/docs/checklists/README.md +++ b/docs/checklists/README.md @@ -1,7 +1,6 @@ Checklists for the implementation of drivers for reading data from diabetes devices currently supported or in development. - * [Abbott FreeStyle Lite & Freedom Lite (BGM)](abbottFreeStyleLite.md) - * [Abbott FreeStyle Libre (CGM & BGM data)](abbottFreeStyleLibre.md) + * [Abbott FreeStyle (BGM)](abbottFreeStyleLite.md) * [Abbott Precision Xtra (blood glucose & ketone meter)](abbottPrecisionXtra.md) * [Animas Vibe (CGM data)](animasCGM.md) * [Animas Ping and Vibe Insulin Pumps](animasPingAndVibe.md) @@ -12,4 +11,4 @@ Checklists for the implementation of drivers for reading data from diabetes devi * [Insulet OmniPod Insulin Delivery System](insuletOmniPod.md) * [OneTouch VerioIQ (BGM)](oneTouchVerioIQ.md) * [Tandem Insulin Pumps](tandem.md) - * [Tandem G4 (CGM data)](tandemCGM.md) + * [Tandem G4 (CGM data)](tandemCGM.md) \ No newline at end of file diff --git a/docs/checklists/abbottFreeStyleLibre.md b/docs/checklists/abbottFreeStyleLibre.md deleted file mode 100644 index 65da87d8a8..0000000000 --- a/docs/checklists/abbottFreeStyleLibre.md +++ /dev/null @@ -1,147 +0,0 @@ -## Checklist for CGM Implementation - -(Key: - - - `[x]` available in data protocol/documented in spec and implemented - - `[-]` available in data protocol/documented in spec but *not* yet implemented - - `[?]` unknown whether available in data protocol/documented in spec; *not* yet implemented - - `*[ ]` TODO: needs implementation! - - `[ ]` unavailable in data protocol and/or not documented in spec and not yet implemented) - -### Required if Present - -#### CBG - - - `[x]` cbg values - - `[ ]` units of cbg values (read from device, not hard-coded) - - `[x]` out-of-range values (LO or HI) - - `[x]` out-of-range value thresholds (e.g., often 40 for low and 400 for high on CGMs) - -Device-specific? (Add any device-specific notes/additions here.) - - internal glucose unit is always mg/dL for this device, independent of display unit - - out-of-range thresholds are 41 mg/dL and 499 mg/dL - - out-of-range measurements are reported as values 40 or 500 respectively - -#### Device Events - - `[ ]` calibrations - - `[ ]` calibration value - - `[ ]` units of calibration value (read from device, not hard-coded) - - `[x]` time changes (presence of which is also in the [BtUTC section](#bootstrapping-to-utc) below) - - `[x]` device display time `from` (before change) and `to` (result of change) - - `[x]` agent of change (`automatic` or `manual`) - - `[ ]` timezone - - `[ ]` reason for change (read from device) - -Device-specific? (Add any device-specific notes/additions here.) - - device does not need calibration - -#### Settings - - - `[x]` units preference for BG display - - `[x]` units of data being uploaded (will be mutated to mmol/L storage units if not mmol/L) - - `[x]` transmitter ID - - `[ ]` low alert settings - - `[ ]` enabled - - `[ ]` level/threshold - - `[ ]` snooze threshold - - `[ ]` high alert settings - - `[ ]` enabled - - `[ ]` level/threshold - - `[ ]` snooze threshold - - `[ ]` rate-of-change alerts - - `[ ]` fall rate alert - - `[ ]` enabled - - `[ ]` rate threshold for alerting - - `[ ]` rise rate alert - - `[ ]` enabled - - `[ ]` rate threshold for alerting - - `[ ]` out-of-range alerts - - `[ ]` enabled - - `[ ]` snooze time between alerts - - `[ ]` predictive alerts - - `[ ]` low prediction - - `[ ]` enabled - - `[ ]` time sensitivity (minutes to predicted low for alerting) - - `[ ]` high prediction - - `[ ]` enabled - - `[ ]` time sensitivity (minutes to predicted high for alerting) - - `[ ]` calibration alerts/reminders - - `[ ]` pre-reminder - - `[ ]` overdue alert - -Settings history: - - - `[ ]` device stores all changes to settings OR - - `[x]` device only returns current settings at time of upload - -No Tidepool data model (yet): volume and/or vibrate mode of all alerts (can/should go in `payload`). - -Device-specific? (Add any device-specific notes/additions here.) - -#### "Bootstrapping" to UTC - - - `[x]` index - - `[ ]` UTC timestamp (*Hey, one can dream!*) OR - - `[x]` internal timestamp or persistent log index (across device communication sessions) to order all pump events (regardless of type), independent of device display time OR - - `[ ]` ephemeral log index (does not persist across device communication sessions) to order all pump events (regardless of type), independent of device display time - - `[x]` date & time settings changes - -Device-specific? (Add any device-specific notes/additions here.) - -### No Tidepool Data Model Yet - -> **NB:** You can and should add to this section if there are other data types documented in the device's data protocol specification but not part of Tidepool's data model (yet). - - - `[-]` activity/exercise - - `[-]` food (e.g., Dexcom allows logging carb events) - - `[-]` notes/other events - - `[-]` insulin (rapid acting, long term) - -### Tidepool ingestion API - -Choose one of the following: - - - `[x]` legacy "jellyfish" ingestion API - - `*[ ]` platform ingestion API - -### Known implementation issues/TODOs - -*Use this space to describe device-specific known issues or implementation TODOs **not** contained in the above datatype-specific sections.* - - -## Checklist for Blood Glucose Meter Implementation - -### Required if Present - -- `[x]` smbg values -- `[ ]` units of smbg values (read from device, not hard-coded) -- `[x]` out-of-range values (LO or HI) -- `[x]` out-of-range value thresholds (e.g., often 20 for low and 600 for high on BGMs) -- `[x]` date & time settings changes -- `[x]` blood ketone values -- `[ ]` units of blood ketone values (read from device, not hard-coded) -- `[x]` ketone out-of-range values -- `[x]` ketone out-of-range value thresholds - -Device-specific? (Add any device-specific notes/additions here.) - - internal glucose unit is always mg/dL for this device, independent of display unit - - glucose out-of-range thresholds are 41 mg/dL and 499 mg/dL - - glucose out-of-range measurements are reported as values 40 or 500 respectively - - ketone out-of-range upper threshold is 8.0 mmol/L - -### No Tidepool Data Model Yet - -- `[x]` control (solution) tests (whether marked in UI or auto-detected) - until we have a data model, these should be discarded -- `[-]` device settings, other than date & time (e.g., target blood glucose range) -- `[-]` tag/note (e.g., pre- vs. post-meal) - -### Tidepool ingestion API - -Choose one of the following: - - - `[x]` legacy "jellyfish" ingestion API - - `*[ ]` platform ingestion API - -### Known implementation issues/TODOs - -*Use this space to describe device-specific known issues or implementation TODOs **not** contained in the above datatype-specific sections.* diff --git a/docs/checklisttemplates/CGMChecklist.md b/docs/checklisttemplates/CGMChecklist.md index aede368eef..07b448b776 100644 --- a/docs/checklisttemplates/CGMChecklist.md +++ b/docs/checklisttemplates/CGMChecklist.md @@ -101,4 +101,8 @@ Choose one of the following: ### Known implementation issues/TODOs +Add any device-specific known issues or implementation TODOs here in checklist format. + +### Known implementation issues/TODOs + *Use this space to describe device-specific known issues or implementation TODOs **not** contained in the above datatype-specific sections.* diff --git a/lib/core/device.js b/lib/core/device.js index 06b03fb67d..55bc226f06 100644 --- a/lib/core/device.js +++ b/lib/core/device.js @@ -37,7 +37,6 @@ var insuletOmniPod = require('../drivers/insulet/insuletDriver'); var oneTouchUltra2 = require('../drivers/onetouch/oneTouchUltra2'); var oneTouchVerioIQ = require('../drivers/onetouch/oneTouchVerioIQ'); var abbottFreeStyleLite = require('../drivers/abbott/abbottFreeStyleLite'); -var abbottFreeStyleLibre = require('../drivers/abbott/abbottFreeStyleLibre'); var bayerContourNext = require('../drivers/bayer/bayerContourNext'); var animasDriver = require('../drivers/animas/animasDriver'); var medtronicDriver = require('../drivers/medtronic/medtronicDriver'); @@ -62,7 +61,6 @@ device._deviceDrivers = { 'OneTouchVerioIQ': oneTouchVerioIQ, 'AbbottFreeStyleLite': abbottFreeStyleLite, 'AbbottFreeStyleFreedomLite': abbottFreeStyleLite, - 'AbbottFreeStyleLibre': abbottFreeStyleLibre, 'BayerContourNext': bayerContourNext, 'BayerContourNextUsb': bayerContourNext, 'BayerContourUsb': bayerContourNext, @@ -79,7 +77,6 @@ device._deviceComms = { 'OneTouchVerioIQ': serialDevice, 'AbbottFreeStyleLite': serialDevice, 'AbbottFreeStyleFreedomLite': serialDevice, - 'AbbottFreeStyleLibre': hidDevice, 'Tandem': serialDevice, 'BayerContourNext': hidDevice, 'BayerContourNextUsb': hidDevice, diff --git a/lib/drivers/abbott/.eslintrc b/lib/drivers/abbott/.eslintrc deleted file mode 100644 index 769cd6b450..0000000000 --- a/lib/drivers/abbott/.eslintrc +++ /dev/null @@ -1,61 +0,0 @@ -{ - "extends": "airbnb", - "parser": "babel-eslint", - "plugins": ["lodash"], - "parserOptions": { - "ecmaVersion": 6 - }, - "rules": { - "no-plusplus": [ - "error", - { - "allowForLoopAfterthoughts": true - } - ] - }, - "overrides": [ - { - "files": [ - "abbottFreeStyleLite.js", - "abbottPrecisionXtra.js" - ], - "rules": { - "func-names": "warn", - "no-var": "warn", - "vars-on-top": "warn", - "no-unused-vars": "warn", - "object-shorthand": "warn", - "comma-dangle": "warn", - "space-before-function-paren": "warn", - "no-param-reassign": "warn", - "prefer-template": "warn", - "no-useless-escape": "warn", - "keyword-spacing": "warn", - "indent": "warn", - "spaced-comment": "warn", - "eqeqeq": "warn", - "space-infix-ops": "warn", - "prefer-arrow-callback": "warn", - "no-shadow": "warn", - "array-bracket-spacing": "warn", - "no-use-before-define": "warn", - "no-else-return": "warn", - "no-bitwise": "warn", - "consistent-return": "warn", - "no-plusplus": "warn", - "no-continue": "warn", - "no-loop-func": "warn", - "object-curly-spacing": "warn", - "key-spacing": "warn", - "padded-blocks": "warn", - "no-console": "warn", - "no-multi-spaces": "warn", - "no-mixed-operators": "warn", - "max-len": "warn" - } - } - ], - "settings": { - "lodash": 3 - } -} \ No newline at end of file diff --git a/lib/drivers/abbott/abbottFreeStyleLibre.js b/lib/drivers/abbott/abbottFreeStyleLibre.js deleted file mode 100644 index 0d2ecd1f42..0000000000 --- a/lib/drivers/abbott/abbottFreeStyleLibre.js +++ /dev/null @@ -1,207 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2017, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import { clone, assign } from 'lodash'; -import async from 'async'; -import sundial from 'sundial'; - -import FreeStyleLibreProtocol from './freeStyleLibreProtocol'; -import FreeStyleLibreData from './freeStyleLibreData'; -import { DB_TABLE_ID, CFG_TABLE_ID, DEVICE_MODEL_NAME, COMPRESSION } from './freeStyleLibreConstants'; - -const isBrowser = typeof window !== 'undefined'; -// eslint-disable-next-line no-console -const debug = isBrowser ? require('bows')('FreeStyleLibreDriver') : console.log; - -export default function (config) { - const cfg = clone(config); - const hidDevice = config.deviceComms; - const protocol = new FreeStyleLibreProtocol(cfg); - const dataParser = new FreeStyleLibreData(cfg); - - return { - /* eslint no-param-reassign: - [ "error", { "props": true, "ignorePropertyModificationsFor": ["data"] } ] */ - detect(deviceInfo, cb) { - cb(null, deviceInfo); - }, - - setup(deviceInfo, progress, cb) { - progress(100); - cb(null, { deviceInfo }); - }, - - connect(progress, data, cb) { - hidDevice.connect(data.deviceInfo, FreeStyleLibreProtocol.probe, (err) => { - if (err) { - return cb(err); - } - return protocol.initCommunication(() => { - // ignore results of init as it seems not to be relevant to the following communication - data.disconnect = false; - progress(100); - return cb(null, data); - }); - }); - }, - - getConfigInfo(progress, data, cb) { - progress(0); - - const getterFunctions = [ - (callback) => { protocol.getSerialNumber(callback); }, - (callback) => { protocol.getFirmwareVersion(callback); }, - (callback) => { protocol.getDBRecordNumber(callback); }, - ]; - let counter = 0; - async.series(getterFunctions, (err, result) => { - counter += 1; - progress(100 * (counter / getterFunctions.length)); - - if (err) { - debug('getConfigInfo: ', err); - return cb(err, null); - } - data.connect = true; - result.forEach((element) => { - if (typeof element === 'object') { - debug('getConfigInfo: result object: ', element); - assign(data.deviceInfo, element); - } - return null; - }); - debug('getConfigInfo: data: ', data); - - return cb(null, data); - }); - }, - - 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); }, - ]; - - data.aapPackets = []; - let counter = 0; - async.series(getterFunctions, (err, results) => { - counter += 1; - progress(100 * (counter / getterFunctions.length)); - - if (err) { - debug('fetchData: error: ', err); - return cb(err, data); - } - results.forEach((aapPackets) => { - if (typeof aapPackets === 'object') { - data.aapPackets = data.aapPackets.concat(aapPackets); - } - }); - return cb(null, data); - }); - }, - - processData(progress, data, cb) { - debug('processData: num aapPackets:', data.aapPackets.length); - progress(0); - - data.deviceInfo.deviceId = `${data.deviceInfo.driverId}-${data.deviceInfo.serialNumber}`; - cfg.builder.setDefaults({ deviceId: data.deviceInfo.deviceId }); - - data.post_records = - dataParser.processAapPackets(data.aapPackets, data.deviceInfo.dbRecordNumber); - debug('processData: num post records:', data.post_records.length); - - progress(100); - data.processData = true; - return cb(null, data); - }, - - uploadData(progress, data, cb) { - debug('uploadData: num post records:', data.post_records.length); - progress(0); - - const sessionInfo = { - deviceTags: ['bgm', 'cgm'], - deviceManufacturers: ['Abbott'], - deviceModel: DEVICE_MODEL_NAME, - deviceSerialNumber: data.deviceInfo.serialNumber, - deviceId: data.deviceInfo.deviceId, - start: sundial.utcDateString(), - timeProcessing: cfg.tzoUtil.type, - tzName: cfg.timezone, - version: cfg.version, - }; - - cfg.api.upload.toPlatform(data.post_records, sessionInfo, progress, cfg.groupId, - (err, result) => { - progress(100); - - if (err) { - debug(err); - debug(result); - return cb(err, data); - } - data.cleanup = true; - return cb(null, data); - }); - }, - - disconnect(progress, data, cb) { - debug('disconnect'); - hidDevice.removeListeners(); - // Due to an upstream bug in HIDAPI on Windoze, we have to send a command - // to the device to ensure that the listeners are removed before we disconnect - // For more details, see https://github.com/node-hid/node-hid/issues/61 - hidDevice.send(FreeStyleLibreProtocol.buildHidPacket(0x00, ''), () => { - progress(100); - cb(null, data); - }); - }, - - cleanup(progress, data, cb) { - debug('cleanup'); - if (!data.disconnect) { - hidDevice.disconnect(data, () => { - progress(100); - data.cleanup = true; - data.disconnect = true; - cb(null, data); - }); - } else { - progress(100); - } - }, - }; -} diff --git a/lib/drivers/abbott/cli/fslibre.js b/lib/drivers/abbott/cli/fslibre.js deleted file mode 100755 index a3be050f52..0000000000 --- a/lib/drivers/abbott/cli/fslibre.js +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env babel-node -/* eslint-disable no-console,no-use-before-define */ - -import program from 'commander'; -import fs from 'fs'; -import async from 'async'; - -import hidDevice from '../../../hidDevice'; -import api from '../../../core/api'; -import device from '../../../core/device'; -import config from '../../../../.config'; -import pkg from '../../../../package.json'; -import builder from '../../../objectBuilder'; -import abbottFreeStyleLibre from '../abbottFreeStyleLibre'; - -import stringify from './stringify'; - -// eslint-disable-next-line no-underscore-dangle -global.__DEBUG__ = true; - -const intro = 'FSLibre CLI:'; -let libreDriver; - -program - .version('0.0.1', null) - .option('-u, --username [user]', 'username') - .option('-p, --password [pw]', 'password') - .option('-t, --timezone [tz]', 'named timezone', config.DEFAULT_TIMEZONE) - .option('-f, --file [path]', 'load deviceInfo and aapPackets from JSON file instead of device') - .option('-o, --output [path]', 'save processed data to JSON file instead of uploading') - .parse(process.argv); - -const options = { - api, - timezone: program.timezone, - version: `${pkg.name} ${pkg.version}`, - builder: builder(), -}; - - -if ((program.username && program.password) || program.output) { - if (program.output) { - device.init(options, initCallback); - } else { - login(program.username, program.password, config); - } -} else { - program.help(); -} - -function login(username, password, cfg) { - console.log(intro, 'login:', cfg.API_URL); - api.create({ - apiUrl: cfg.API_URL, - uploadUrl: cfg.UPLOAD_URL, - dataUrl: cfg.DATA_URL, - version: 'uploader node CLI tool - fslibre', - }); - api.init(() => { - api.user.login({ username, password }, loginCallback); - }); -} - -function loginCallback(error, loginData) { - if (error) { - console.log(intro, 'loginCallback: Failed authentication!'); - console.log(error); - process.exit(); - } - console.log(intro, 'loginCallback:', 'Uploading using the timezone', program.timezone); - console.log(intro, 'loginCallback:', 'Uploading for user ', loginData.userid); - - console.log(intro, 'loginCallback:', 'Starting connection to device...'); - options.targetId = loginData.userid; - options.groupId = loginData.userid; - device.init(options, initCallback); -} - -function readDataFromFile() { - console.log(intro, 'Reading JSON data from:', program.file); - return JSON.parse(fs.readFileSync(program.file, 'utf8'), (k, v) => { - if (v !== null && typeof v === 'object' && 'type' in v && - v.type === 'Buffer' && 'data' in v && Array.isArray(v.data)) { - // re-create Buffer objects for data fields of aapPackets - return new Buffer(v.data); - } - return v; - }); -} - -function initCallback() { - if (program.file) { - const data = readDataFromFile(); - - console.log(intro, 'Processing AAP packets, length:', data.aapPackets.length); - libreDriver = abbottFreeStyleLibre(options); - libreDriver.processData(progress => progress, data, processCallback); - } else { - device.detect('AbbottFreeStyleLibre', options, detectCallback); - } -} - -function processCallback(error, data) { - if (error) { - console.log(intro, 'processCallback: Failed:', error); - process.exit(); - } - - console.log(intro, 'Num post records:', data.post_records.length); - - if (program.output) { - writeDataToFile(data, done); - } else { - libreDriver.uploadData(progress => progress, data, uploadCallback); - } -} - -function detectCallback(error, deviceInfo) { - if (deviceInfo !== undefined) { - console.log(intro, 'detectCallback:', 'deviceInfo: ', deviceInfo); - options.deviceInfo = deviceInfo; - if (program.output) { - copyDataFromDeviceToFile(deviceInfo); - } else { - device.upload('AbbottFreeStyleLibre', options, uploadCallback); - } - } else { - console.error(intro, 'detectCallback:', 'Could not find FreeStyle Libre device. Is it connected via USB?'); - console.error(intro, 'detectCallback:', `Error value: ${error}`); - } -} - -function copyDataFromDeviceToFile(deviceInfo) { - options.deviceComms = hidDevice(); - libreDriver = abbottFreeStyleLibre(options); - async.waterfall([ - libreDriver.setup.bind(libreDriver, deviceInfo, () => {}), - libreDriver.connect.bind(libreDriver, () => {}), - libreDriver.getConfigInfo.bind(libreDriver, () => {}), - libreDriver.fetchData.bind(libreDriver, () => {}), - libreDriver.processData.bind(libreDriver, () => {}), - // no call to the upload function here, since we only want to download the data from the device - libreDriver.disconnect.bind(libreDriver, () => {}), - ], (err, resultOptional) => { - const result = resultOptional || {}; - libreDriver.cleanup(() => {}, result, () => { - writeDataToFile(result, done); - }); - }); -} - -function uploadCallback(error) { - if (error) { - console.log(intro, 'uploadCallback:', 'error: ', error); - process.exit(); - } - done(); -} - -function writeDataToFile(data, callback) { - console.log(intro, 'uploadCallback:', 'writing data to file:', program.output); - fs.writeFile(program.output, stringify(data, { indent: 2, maxLevelPretty: 3 }), 'utf8', callback); -} - -function done() { - console.log(intro, 'Done!'); - process.exit(); -} diff --git a/lib/drivers/abbott/cli/stringify.js b/lib/drivers/abbott/cli/stringify.js deleted file mode 100644 index a70d7e040b..0000000000 --- a/lib/drivers/abbott/cli/stringify.js +++ /dev/null @@ -1,81 +0,0 @@ -// a wrapper around JSON.stringify to allow for more control over the formatting of the JSON - -export default function stringify(obj, optionsOptional) { - const stringOrChar = /("(?:[^\\"]|\\.)*")|[:,]/g; - - function prettify(string) { - return string.replace(stringOrChar, (match, str) => (str ? match : `${match} `)); - } - - function get(opt, name, defaultValue) { - return (name in opt ? opt[name] : defaultValue); - } - - const options = optionsOptional || {}; - const indent = JSON.stringify([1], null, get(options, 'indent', 2)).slice(2, -3); - const maxLength = (indent === '' ? Infinity : get(options, 'maxLength', 80)); - const maxLevelPretty = get(options, 'maxLevelPretty', Infinity); - - return (function _stringify(objectParam, currentIndent, reserved) { - let object = objectParam; - if (object && typeof object.toJSON === 'function') { - object = object.toJSON(); - } - - const string = JSON.stringify(object); - - if (string === undefined) { - return string; - } - - const currentLevel = currentIndent.length / indent.length; - if (currentLevel >= maxLevelPretty) { - return string; - } - - const length = maxLength - currentIndent.length - reserved; - - if (string.length <= length) { - const prettified = prettify(string); - if (prettified.length <= length) { - return prettified; - } - } - - if (typeof object === 'object' && object !== null) { - const nextIndent = currentIndent + indent; - const items = []; - let delimiters; - const comma = (array, index) => (index === array.length - 1 ? 0 : 1); - - if (Array.isArray(object)) { - for (let index = 0; index < object.length; index++) { - items.push( - _stringify(object[index], nextIndent, comma(object, index)) || 'null', - ); - } - delimiters = '[]'; - } else { - Object.keys(object).forEach((key, index, array) => { - const keyPart = `${JSON.stringify(key)}: `; - const value = _stringify(object[key], nextIndent, - keyPart.length + comma(array, index)); - if (value !== undefined) { - items.push(keyPart + value); - } - }); - delimiters = '{}'; - } - - if (items.length > 0) { - return [ - delimiters[0], - indent + items.join(`,\n${nextIndent}`), - delimiters[1], - ].join(`\n${currentIndent}`); - } - } - - return string; - }(obj, '', 0)); -} diff --git a/lib/drivers/abbott/freeStyleLibreConstants.js b/lib/drivers/abbott/freeStyleLibreConstants.js deleted file mode 100644 index 01bcf5c618..0000000000 --- a/lib/drivers/abbott/freeStyleLibreConstants.js +++ /dev/null @@ -1,487 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2017, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -export const DEVICE_MODEL_NAME = 'FreeStyle Libre'; - -export const KETONE_VALUE_FACTOR = 18.0; // according to specs -export const KETONE_HI = 8.0; -export const KETONE_LO = null; // ketone value cannot be low - -export const GLUCOSE_HI = 500; -export const GLUCOSE_LO = 40; - -export const COMMAND = { - INIT_REQUEST_1: 0x04, - INIT_REQUEST_2: 0x05, - INIT_REQUEST_3: 0x15, - INIT_REQUEST_4: 0x01, - BINARY_REQUEST: 0x0a, - BINARY_RESPONSE: 0x0b, - ACK_FROM_DEVICE: 0x0c, - ACK_FROM_HOST: 0x0d, - TEXT_REQUEST: 0x21, - TEXT_RESPONSE: 0x60, -}; - -export const OP_CODE = { - GET_DATABASE: 0x31, - GET_DB_SCHEMA: 0x34, - COMPRESSED_DATABASE: 0x35, - GET_DATE_TIME: 0x41, - GET_CFG_DATA: 0x51, - GET_CFG_SCHEMA: 0x54, - SET_COMPRESSION: 0x60, - FLUSH_BUFFERS: 0x7d, - ERROR: 0x7e, -}; - -export const ERROR = { - OK: 0, - BAD_PARAMS: 1, - WRONG_PARAM_NUM: 2, - CMD_FAILED: 3, - CRC_ERROR: 4, - INVALID_ID: 5, -}; - -export const ERROR_DESCRIPTION = {}; -ERROR_DESCRIPTION[ERROR.OK] = 'OK: No Error.'; -ERROR_DESCRIPTION[ERROR.BAD_PARAMS] = 'Bad parameter values / out of range!'; -ERROR_DESCRIPTION[ERROR.WRONG_PARAM_NUM] = 'Wrong number of parameters!'; -ERROR_DESCRIPTION[ERROR.CMD_FAILED] = 'Command failed!'; -ERROR_DESCRIPTION[ERROR.CRC_ERROR] = 'Wrong checksum!'; -ERROR_DESCRIPTION[ERROR.INVALID_ID] = 'Wrong table ID!'; - -// for CFG and DB fields -export const FIELD_TYPE = { - UNSIGNED_DECIMAL: 0, // uint - UNSIGNED_HEX: 1, // uint (display as hex) - SIGNED_DECIMAL: 2, // int - UTF8: 3, // string - BULK_DATA: 4, // bytes - CRC32: 10, // uint32 - CRC16: 11, // uint16 -}; - -export const DB_TABLE_ID = { - GLUCOSE_RESULT: 0, - RAPID_ACTING_INSULIN: 1, - HISTORICAL_DATA: 6, - EVENT: 7, -}; - -export const DB_RECORD_TYPE = { - COMMON_HEADER: 255, - GLUCOSE_KETONE_SERVING: 0, - GLUCOSE_KETONE_MEAL: 1, - GLUCOSE_KETONE_CARBS: 2, - UNLOGGED_INSULIN: 3, - CONTROL_SOL_TEST: 4, - TIME_CHANGE_RESULT: 5, - INSULIN_CALC: 6, - INSULIN_MANUAL: 7, - HISTORICAL_DATA: 12, - RESULT_RECORD_WRAP: 13, - INSULIN_WRAP: 14, - HISTORICAL_WRAP: 17, - ERROR: 32, - LOW_BATTERY: 33, - DEAD_BATTERY: 34, - TIME_CHANGE: 35, - LOST_TIME: 36, - INSULIN_SETUP_1: 37, - INSULIN_SETUP_2: 38, - INSULIN_SETUP_3: 39, - INSULIN_SETUP_4: 40, - RESTORE_CONFIG: 41, - CLEAR_RESULT_DB: 42, - CLEAR__SCAN_DB: 43, - CLEAR_ACTIVATION_DB: 44, - CLEAR_HISTORICAL_DB: 45, - USER_TIME_CHANGE: 46, - CLEAR_EVENT_DB: 47, - RECOVERY_EVENT: 48, - MICROCONTROLLER_RESET_EVENT: 49, - MASKED_MODE_STATUS_EVENT: 50, - SENSOR_EXPIRED_EVENT: 51, - EVENT_DATABASE_RECORD_NUMBER_WRAP: 52, -}; - -export const DB_WRAP_RECORDS = {}; -DB_WRAP_RECORDS[DB_RECORD_TYPE.RESULT_RECORD_WRAP] = DB_TABLE_ID.GLUCOSE_RESULT; -DB_WRAP_RECORDS[DB_RECORD_TYPE.INSULIN_WRAP] = DB_TABLE_ID.RAPID_ACTING_INSULIN; -DB_WRAP_RECORDS[DB_RECORD_TYPE.HISTORICAL_WRAP] = DB_TABLE_ID.HISTORICAL_DATA; -DB_WRAP_RECORDS[DB_RECORD_TYPE.EVENT_DATABASE_RECORD_NUMBER_WRAP] = DB_TABLE_ID.EVENT; - -export const DB_FIELD_ID = { - RECORD_NUMBER: 0, - TIME_VALID: 7, - TYPE: 8, - READER_TIME: 9, - USER_TIME_OFFSET: 10, - RESULT: 20, - RESULT_STATUS: 61, - TEST_TYPE: 23, - SMART_TAGS: 24, - LONG_ACTING_INSULIN_RECORDED: 25, - RAPID_ACTING_INSULIN: 26, - MEDICATION: 28, - EXERCISE: 29, - TREND: 30, - RAI_RECORD_POINTER: 32, - RAI_DATA_FLAG: 33, - LONG_ACTING_INSULIN: 34, - GLYCEMIC_ALARM: 35, - GRAMS_PER_SERVING: 36, - SERVING_COUNT: 37, - MEAL_TYPE: 38, - GRAMS_OF_CARB: 39, - FOOD_DATA_FLAG: 40, - FOOD_QUICK_TAG: 41, - RESULT_OLD_READER_TIME: 42, - RESULT_OLD_USER_OFFSET: 43, - RESULT_OLD_VALID: 44, - DATA_CRC: 49, - SMART_TAG_0: 50, - SMART_TAG_1: 51, - SMART_TAG_2: 52, - SMART_TAG_3: 53, - SMART_TAG_4: 54, - SMART_TAG_5: 55, - SMART_TAG_CRC: 56, - RESULT_DATA_QUALITY_ERROR: 57, - RESULT_UNLOGGED_INSULIN: 58, - EFFECTIVE_TIME: 59, - WRAP_RECORD_NUMBER: 60, - RAI_RESULT: 70, - SIGNED_OVERRIDE: 71, - SIGNED_CORRECTION: 72, - IOB: 73, - MEAL_INSULIN: 74, - UNLOGGED_INSULIN: 75, - TIME_OFFSET: 76, - INSULIN_CRC: 77, - MANUAL_RAI_RESULT: 78, - INSULIN_RECORD_WARP: 79, - GLUCOSE: 150, - FIRST_FLAG: 151, - TIME_CHANGE: 152, - FOOD_FLAG: 153, - HISTORICAL_RAPID_ACTING_INSULIN: 154, - HISTORICAL_CRC: 155, - HISTORICAL_DATA_QUALITY_ERROR: 156, - LIFE_COUNTER: 157, - HISTORICAL_DATA_WRAP: 158, - ERROR_CODE: 160, - ERROR_DATA: 161, - OLD_READER_TIME: 165, - OLD_USER_OFFSET: 166, - VALID: 167, - RTC_COUNTER: 168, - HIGH_CORRECTION: 203, - LOW_CORRECTION: 204, - CORRECTION_FACTOR: 205, - CALCULATOR_OFF_FLAG: 237, - FIXED_DOSE_BREAKFAST: 206, - FIXED_DOSE_LUNCH: 207, - FIXED_DOSE_DINNER: 208, - MORNING_CARB_INSULIN: 209, - MID_DAY_CARB_INSULIN: 210, - EVENING_CARB_INSULIN: 211, - NIGHT_CARB_INSULIN: 212, - MORNING_UNITS: 213, - MID_DAY_UNITS: 214, - EVENING_UNITS: 215, - NIGH_UNITS: 216, - GRAMS_UNITS: 217, - CARBS: 218, - UNITS_TIME_OF_DAY_BLOCK: 219, - CARB_TIME_OF_DAY_BLOCK: 220, - MORNING_HIGH_CORRECTION: 221, - MORNING_LOW_CORRECTION: 222, - MID_DAY_HIGH_CORRECTION: 223, - MID_DAY_LOW_CORRECTION: 224, - EVENING_HIGH_CORRECTION: 225, - EVENING_LOW_CORRECTION: 226, - NIGHT_HIGH_CORRECTION: 227, - NIGHT_LOW_CORRECTION: 228, - CORRECTION_TIME_OF_DAY_BLOCK: 229, - MORNING_FACTOR: 230, - MID_DAY_FACTOR: 231, - EVENING_FACTOR: 232, - NIGHT_FACTOR: 233, - INSULIN_DURATION: 234, - TARGET_TIME_OF_DAY_BLOCK: 235, - BOB_SYMBOL: 236, - PATIENT_CONFIG: 170, - FACTORY_CONFIG: 171, - CAL_CONFIG_CLEAR: 172, - STRIP_COUNT: 239, - SCAN_COUNTER: 241, - ACTIVATION_COUNT: 242, - RECOVERY_ITEM: 175, - LOW_VOLTAGE: 181, - LOSS_OF_CLOCK: 182, - LOSS_OF_LOCK: 183, - WATCHDOG: 184, - EXTERNAL_PIN_RESET: 185, - POWER_ON_RESET: 186, - JTAG_RESET: 187, - LOCK_UP_ARM: 188, - SOFTWARE_RESET: 189, - MDM_AP_RESET: 190, - EZ_PORT_RESET: 191, - STOP_MODE_RESET: 192, - OLD_MASK_MODE: 193, - NEW_MASK_MODE: 194, - PDU_STATE: 195, - WRITTEN_BY_UI: 196, - EVENT_WRAP: 197, - EVENT_CRC: 253, -}; - -export const CFG_TABLE_ID = { - METER_FACTORY_CONFIGURATION: 1, - METER_SETTINGS: 2, - USER_PATIENT_CONFIGURATION: 3, - USER_PATIENT_SETTINGS: 4, - INSULIN_SETTINGS: 5, - REMINDER_STRING: 6, - SMART_TAG_NOTES: 7, - STORED_SENSOR_INFORMATION: 9, - REMINDER_DATA: 10, -}; - -export const CFG_FIELD_ID = { - SYSTEM_TYPE: 1025, - MARKET_LEVEL: 1026, - MARKET_SUB_LEVEL: 1027, - MARKET_PUCK_LEVEL: 1028, - BRAND_NAME: 1029, - NUMBER_FORMAT: 1056, - UNIT_OF_MEASURE: 1057, - INSULIN_CALC_PRESENT: 1058, - ALLOWABLE_MEAL_UNIT: 1059, - GLYCEMIC_RANGE_HIGH: 1067, - GLYCEMIC_RANGE_LOW: 1068, - TIME_CONVERSION: 1072, - FIRST_TIME_STARTUP_DONE: 1106, - PATIENT_NAME: 1130, - PATIENT_ID: 1131, - TARGET_RANGE_LOW: 1132, - TARGET_RANGE_HIGH: 1133, - LANGUAGE_SETTING: 1134, - TIME_FORMAT: 1135, - INSULIN_DOSE_INCREMENT: 1136, - MASK_MODE_OPTION: 1137, - MASK_MODE_REMINDER: 1138, - MASK_MODE_HOUR: 1139, - MASK_MODE_MIN: 1140, - BEEPER_VOLUME: 1160, - NOTIFICATION_SOUND: 1161, - NOTIFICATION_VIBE: 1162, - BUTTON_SOUND: 1163, - INSULIN_MODE: 1190, - EASY_DONE_FLAG: 1191, - ADVANCE_DONE_FLAG: 1192, - CARB_TYPE: 1193, - CARBS_PER_SERVING: 1194, - CARB_RATIO_FLAG: 1195, - CARB_RATIO_ALL_DAY: 1196, - CARB_RATIO_MORNING: 1197, - CARB_RATIO_MIDDAY: 1198, - CARB_RATIO_EVENING: 1199, - CARB_RATIO_NIGHT: 1200, - SERVING_RATIO_FLAG: 1201, - SERVING_RATIO_ALL_DAY: 1202, - SERVING_RATIO_MORNING: 1203, - SERVING_RATIO_MIDDAY: 1204, - SERVING_RATIO_EVENING: 1205, - SERVING_RATIO_NIGHT: 1206, - IOB_ICON_FLAG: 1207, - INSULIN_DURATION: 1208, - FIXED_DOSE_BREAKFAST: 1209, - FIXED_DOSE_LUNCH: 1210, - FIXED_DOES_DINNER: 1211, - CORRECTION_FACTORS_REQ: 1212, - CORRECTION_TYPE: 1213, - SINGLE_TARGET_RATIO_FLAG: 1214, - SINGLE_TARGET_RATIO_ALL_DAY: 1215, - SINGLE_TARGET_RATIO_MORNING: 1216, - SINGLE_TARGET_RATIO_MIDDAY: 1217, - SINGLE_TARGET_RATIO_EVENING: 1218, - SINGLE_TARGET_RATIO_NIGHT: 1219, - LOW_TARGET_RATIO_FLAG: 1220, - LOW_TARGET_RATIO_ALL_DAY: 1221, - LOW_TARGET_RATIO_MORNING: 1222, - LOW_TARGET_RATIO_MIDDAY: 1223, - LOW_TARGET_RATIO_EVENING: 1224, - LOW_TARGET_RATIO_NIGHT: 1225, - HIGH_TARGET_RATIO_FLAG: 1226, - HIGH_TARGET_RATIO_ALL_DAY: 1227, - HIGH_TARGET_RATIO_MORNING: 1228, - HIGH_TARGET_RATIO_MIDDAY: 1229, - HIGH_TARGET_RATIO_EVENING: 1230, - HIGH_TARGET_RATIO_NIGHT: 1231, - BG_DROP_CORRECTION_RATIO_FLAG: 1232, - BG_DROP_ALL_DAY: 1233, - BG_DROP_MORNING: 1234, - BG_DROP_MIDDAY: 1235, - BG_DROP_EVENING: 1236, - BG_DROP_NIGHT: 1237, - REMINDER_STRING_0: 1260, - REMINDER_STRING_1: 1261, - REMINDER_STRING_2: 1262, - REMINDER_STRING_3: 1263, - REMINDER_STRING_4: 1264, - REMINDER_STRING_5: 1265, - REMINDER_STRING_6: 1266, - REMINDER_STRING_7: 1267, - REMINDER_STRING_8: 1268, - REMINDER_STRING_9: 1269, - REMINDER_STRING_10: 1270, - REMINDER_STRING_11: 1271, - SMART_TAG_0: 1290, - SMART_TAG_1: 1291, - SMART_TAG_2: 1292, - SMART_TAG_3: 1293, - SMART_TAG_4: 1294, - SMART_TAG_5: 1295, - BULK_STORAGE: 1310, - SENSOR_STATE: 1320, - SENSOR_UID: 1321, - PAIRED_FLAG: 1322, - SENSOR_PUCK_INFO: 1323, - SENSOR_START_TIME: 1324, - REMINDER_CUSTOM_STRING_1: 1400, - REMINDER_CUSTOM_STRING_2: 1401, - REMINDER_CUSTOM_STRING_3: 1402, - REMINDER_CUSTOM_STRING_4: 1403, - REMINDER_CUSTOM_STRING_5: 1404, - REMINDER_CUSTOM_STRING_6: 1405, - REMINDER_CUSTOM_STRING_7: 1406, - REMINDER_CUSTOM_STRING_8: 1407, - REMINDER_CUSTOM_STRING_9: 1408, - REMINDER_1_DATA_TYPE: 1415, - REMINDER_1_DATA_STRING_INDEX: 1416, - REMINDER_1_DATA_TIME_HOUR: 1417, - REMINDER_1_DATA_TIME_MINUTE: 1418, - REMINDER_1_DATA_TIME_SECOND: 1419, - REMINDER_1_DATA_STATUS: 1475, - REMINDER_2_DATA_TYPE: 1420, - REMINDER_2_DATA_STRING_INDEX: 1421, - REMINDER_2_DATA_TIME_HOUR: 1422, - REMINDER_2_DATA_TIME_MINUTE: 1423, - REMINDER_2_DATA_TIME_SECOND: 1424, - REMINDER_2_DATA_STATUS: 1476, - REMINDER_3_DATA_TYPE: 1425, - REMINDER_3_DATA_STRING_INDEX: 1426, - REMINDER_3_DATA_TIME_HOUR: 1427, - REMINDER_3_DATA_TIME_MINUTE: 1428, - REMINDER_3_DATA_TIME_SECOND: 1429, - REMINDER_3_DATA_STATUS: 1477, - REMINDER_4_DATA_TYPE: 1430, - REMINDER_4_DATA_STRING_INDEX: 1431, - REMINDER_4_DATA_TIME_HOUR: 1432, - REMINDER_4_DATA_TIME_MINUTE: 1433, - REMINDER_4_DATA_TIME_SECOND: 1434, - REMINDER_4_DATA_STATUS: 1478, - REMINDER_5_DATA_TYPE: 1435, - REMINDER_5_DATA_STRING_INDEX: 1436, - REMINDER_5_DATA_TIME_HOUR: 1437, - REMINDER_5_DATA_TIME_MINUTE: 1438, - REMINDER_5_DATA_TIME_SECOND: 1439, - REMINDER_5_DATA_STATUS: 1479, - REMINDER_6_DATA_TYPE: 1440, - REMINDER_6_DATA_STRING_INDEX: 1441, - REMINDER_6_DATA_TIME_HOUR: 1442, - REMINDER_6_DATA_TIME_MINUTE: 1443, - REMINDER_6_DATA_TIME_SECOND: 1444, - REMINDER_6_DATA_STATUS: 1480, - REMINDER_7_DATA_TYPE: 1445, - REMINDER_7_DATA_STRING_INDEX: 1446, - REMINDER_7_DATA_TIME_HOUR: 1447, - REMINDER_7_DATA_TIME_MINUTE: 1448, - REMINDER_7_DATA_TIME_SECOND: 1449, - REMINDER_7_DATA_STATUS: 1481, - REMINDER_8_DATA_TYPE: 1450, - REMINDER_8_DATA_STRING_INDEX: 1451, - REMINDER_8_DATA_TIME_HOUR: 1452, - REMINDER_8_DATA_TIME_MINUTE: 1453, - REMINDER_8_DATA_TIME_SECOND: 1454, - REMINDER_8_DATA_STATUS: 1482, - REMINDER_9_DATA_TYPE: 1455, - REMINDER_9_DATA_STRING_INDEX: 1456, - REMINDER_9_DATA_TIME_HOUR: 1457, - REMINDER_9_DATA_TIME_MINUTE: 1458, - REMINDER_9_DATA_TIME_SECOND: 1459, - REMINDER_9_DATA_STATUS: 1483, - REMINDER_10_DATA_TYPE: 1460, - REMINDER_10_DATA_STRING_INDEX: 1461, - REMINDER_10_DATA_TIME_HOUR: 1462, - REMINDER_10_DATA_TIME_MINUTE: 1463, - REMINDER_10_DATA_TIME_SECOND: 1464, - REMINDER_10_DATA_STATUS: 1484, - REMINDER_11_DATA_TYPE: 1465, - REMINDER_11_DATA_STRING_INDEX: 1466, - REMINDER_11_DATA_TIME_HOUR: 1467, - REMINDER_11_DATA_TIME_MINUTE: 1468, - REMINDER_11_DATA_TIME_SECOND: 1469, - REMINDER_11_DATA_STATUS: 1485, - REMINDER_12_DATA_TYPE: 1470, - REMINDER_12_DATA_STRING_INDEX: 1471, - REMINDER_12_DATA_TIME_HOUR: 1472, - REMINDER_12_DATA_TIME_MINUTE: 1473, - REMINDER_12_DATA_TIME_SECOND: 1474, - REMINDER_12_DATA_STATUS: 1486, - CONFIG_CRC: 2047, -}; - -export const RESULT_VALUE_TYPE = { - GLUCOSE: 0, - KETONE: 1, - SCAN: 2, -}; - -export const COMPRESSION = { - DISABLED: 0, - ENABLED: 1, -}; - -export const COMPRESSION_TYPE = { - UNCOMPRESSED: 0xfd, - ZERO_COMPRESSED: 0xff, -}; - -export const CRC32_TABLE = [ - 0x00000000, - 0x04c11db7, - 0x09823b6e, - 0x0d4326d9, - 0x130476dc, - 0x17c56b6b, - 0x1a864db2, - 0x1e475005, - 0x2608edb8, - 0x22c9f00f, - 0x2f8ad6d6, - 0x2b4bcb61, - 0x350c9b64, - 0x31cd86d3, - 0x3c8ea00a, - 0x384fbdbd, -]; diff --git a/lib/drivers/abbott/freeStyleLibreData.js b/lib/drivers/abbott/freeStyleLibreData.js deleted file mode 100644 index dcd7099cf2..0000000000 --- a/lib/drivers/abbott/freeStyleLibreData.js +++ /dev/null @@ -1,466 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2017, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import _ from 'lodash'; -import sundial from 'sundial'; - -import structJs from '../../struct'; -import TZOUtil from '../../TimezoneOffsetUtil'; -import annotate from '../../eventAnnotations'; - -import { - OP_CODE, - ERROR_DESCRIPTION, - DB_TABLE_ID, - DB_WRAP_RECORDS, - DB_RECORD_TYPE, - CFG_TABLE_ID, - RESULT_VALUE_TYPE, - COMPRESSION_TYPE, - KETONE_VALUE_FACTOR, - KETONE_HI, - KETONE_LO, - GLUCOSE_HI, - GLUCOSE_LO, -} from './freeStyleLibreConstants'; - -const struct = structJs(); - -const isBrowser = typeof window !== 'undefined'; -// eslint-disable-next-line no-console -const debug = isBrowser ? require('bows')('FreeStyleLibreDriver') : console.log; - -const FORMAT = { - ERROR: 'bb', - DATE_TIME: 'bbbbbsb', - RECORD_HEADER: 'sbbin', - HISTORICAL_DATA: 'ssss', - TIME_CHANGE: 'insss', // despite the specs, user time offset is a signed value, same as in header -}; - -const FORMAT_LENGTH = _.mapValues(FORMAT, format => struct.structlen(format)); - -const OP_CODE_PROCESSING_ORDER = [ - OP_CODE.GET_CFG_SCHEMA, // not used for now - OP_CODE.GET_DB_SCHEMA, // not used for now - OP_CODE.GET_DATE_TIME, - OP_CODE.GET_CFG_DATA, - OP_CODE.COMPRESSED_DATABASE, - OP_CODE.GET_DATABASE, -]; - -export default class FreeStyleLibreData { - constructor(cfg) { - this.cfg = cfg; - - this.opCodeHandlers = {}; - this.opCodeHandlers[OP_CODE.GET_DATE_TIME] = this.handleDateTime.bind(this); - this.opCodeHandlers[OP_CODE.GET_DB_SCHEMA] = this.handleDatabaseSchema.bind(this); - this.opCodeHandlers[OP_CODE.COMPRESSED_DATABASE] = this.handleCompressedDatabase.bind(this); - this.opCodeHandlers[OP_CODE.GET_DATABASE] = this.handleDatabase.bind(this); - this.opCodeHandlers[OP_CODE.GET_CFG_SCHEMA] = this.handleConfigSchema.bind(this); - this.opCodeHandlers[OP_CODE.GET_CFG_DATA] = this.handleConfigData.bind(this); - this.opCodeHandlers[OP_CODE.ERROR] = this.constructor.handleError; - } - - processAapPackets(aapPackets, dbRecordNumber) { - this.factoryConfig = {}; - this.deviceDateTime = null; - this.records = []; - this.postRecords = []; - - this.dbRecordNumberNextWrap = {}; - this.numResultRecords = 0; - - // calculate next DB record number wrap, so record numbers can be recovered on truncated DBs - const nextWrap = Math.ceil(dbRecordNumber / 0x10000) * 0x10000; - this.dbRecordNumberNextWrap[DB_TABLE_ID.GLUCOSE_RESULT] = nextWrap; - this.dbRecordNumberNextWrap[DB_TABLE_ID.RAPID_ACTING_INSULIN] = nextWrap; - this.dbRecordNumberNextWrap[DB_TABLE_ID.HISTORICAL_DATA] = nextWrap; - this.dbRecordNumberNextWrap[DB_TABLE_ID.EVENT] = nextWrap; - - // start with newest record number and search backwards when processing the DB records - this.oldestResultRecordNumber = dbRecordNumber; - - // sort AAP packets by their OP code - const aapPacketsByOpCode = {}; - aapPackets.forEach((aapPacket) => { - const opCode = aapPacket.opCode; - if (!(opCode in aapPacketsByOpCode)) { - aapPacketsByOpCode[opCode] = []; - } - aapPacketsByOpCode[opCode].push(aapPacket); - }); - - // process AAP packet in fixed order to make sure data is available when needed - OP_CODE_PROCESSING_ORDER.forEach((opCode) => { - if (opCode in aapPacketsByOpCode) { - aapPacketsByOpCode[opCode].forEach((aapPacket) => { - const handler = this.opCodeHandlers[aapPacket.opCode]; - if (handler) { - handler(aapPacket); - } else { - debug('processAapPackets: no handler found for OP code:', aapPacket.opCode); - } - }); - } - }); - - // 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 - const oldestValidRecordNumber = - Math.max(0, this.oldestResultRecordNumber - Math.max(0, 1000 - this.numResultRecords)); - - // use only records that are newer than the oldestValidRecordNumber - // older records cannot be properly timestamped due to potentially truncated time change records - this.records = - this.records.filter(elem => elem.headerFields.recordNumber >= oldestValidRecordNumber); - - if (this.records.length === 0) { - debug('processAapPackets: no valid database records found in', - aapPackets.length, 'AAP packets.'); - return []; - } - - // sort records ascending by record number to honor the timeChangeFlag - this.records.sort((a, b) => a.headerFields.recordNumber - b.headerFields.recordNumber); - - // if timeChangeFlag is set, set record number of previous history record lower than the - // previous time change record - // this prevents these records from being bootstrapped with the wrong timezone - let previousTimeChangeRecord = null; - let previousHistoryRecord = null; - this.records.forEach((record) => { - if (record.historyFields) { - if (record.historyFields.timeChangeFlag && previousTimeChangeRecord) { - previousHistoryRecord.headerFields.recordNumber = - previousTimeChangeRecord.headerFields.recordNumber - 1; - } - previousHistoryRecord = record; - } else if (record.timeChangeFields) { - previousTimeChangeRecord = record; - } - }); - - // sort records again ascending by record number to find the most recent one - this.records.sort((a, b) => a.headerFields.recordNumber - b.headerFields.recordNumber); - const timestamp = this.records[this.records.length - 1].jsDate; - const mostRecent = sundial.applyTimezone(timestamp, this.cfg.timezone).toISOString(); - - this.buildTimeChangeRecords(); - this.cfg.tzoUtil = new TZOUtil(this.cfg.timezone, mostRecent, this.postRecords); - - this.buildCBGRecords(); - this.buildMeasurementRecords(); - - return this.postRecords; - } - - static handleError(aapPacket) { - const fields = struct.unpack(aapPacket.data, 0, FORMAT.ERROR, ['opCode', 'errorCode']); - debug('handleError:', ERROR_DESCRIPTION[fields.errorCode], 'for OP code', fields.opCode); - if (aapPacket.data.length > FORMAT_LENGTH.ERROR) { - debug('handleError: extra data:', aapPacket.data.slice(FORMAT_LENGTH.ERROR).toString('hex')); - } - } - - handleDateTime(aapPacket) { - if (aapPacket.dataLength !== FORMAT_LENGTH.DATE_TIME) { - debug('handleDateTime: wrong data length:', aapPacket.dataLength, 'instead of', FORMAT_LENGTH.DATE_TIME); - return; - } - const fields = struct.unpack(aapPacket.data, 0, FORMAT.DATE_TIME, - ['second', 'minute', 'hour', 'day', 'month', 'year', 'valid']); - if (fields.valid !== 1) { - debug('handleDateTime: date not marked as valid:', fields.valid, aapPacket.data.data[0]); - return; - } - this.deviceDateTime = new Date(fields.year, fields.month - 1, fields.day, - fields.hour, fields.minute, fields.second); - debug('handleDateTime: datetime:', this.deviceDateTime); - } - - // eslint-disable-next-line no-unused-vars,class-methods-use-this - handleDatabaseSchema(aapPacket) { - /* - * These are ignored for now, as the schemata are already known from the specs. - * For now they are hardcoded based on the specs for the few record types that are actually - * needed. - * - * The schemata describe the fields in the database records, so that using this information to - * parse the records instead of the hardcoded format strings, would make it possible to - * understand the data even after a potential firmware upgrade that changes the database - * structure. - * (As long as the field IDs stay the same, the fields parsed via these schemata can still be - * evaluated properly.) - * - * Schema description: (example: the record header prefixed to all records) - * - UINT8 RecordHeader_schema[] = - { - // schema descriptor - 48, 0, // [uint16_le] schema table length (including this descriptor) - 1, 0, // [uint16_le] schema table version - 255, // [uint8] schema table/record ID - 6, 0, // [uint16_le] number of data words (16bit) in the record - 5, // [uint8] number of fields in the record - - // field descriptors (8 byte each) - // [uint16_le], [uint16_le], [uint8], [uint8], [uint16_le] - // field ID, word offset, bit offset inside the word, data type, data length in bits - 0,0,0,0,0,1,16,0, - 8,0,1,0,0,0,8,0, - 7,0,1,0,15,0,1,0, - 9,0,2,0,0,0,32,0, - 10,0,4,0,0,2,32,0 - }; - * - */ - } - - getDateTime(readerTime, userTimeOffset) { - const unixTimestamp = this.factoryConfig.timeConversion + readerTime + userTimeOffset; - return new Date(unixTimestamp * 1000); - } - - buildTimeChangeRecords() { - this.records.filter(elem => elem.headerFields.recordType === DB_RECORD_TYPE.TIME_CHANGE_RESULT) - .forEach((record) => { - const oldDateTime = this.getDateTime(record.timeChangeFields.oldReaderTime, - record.timeChangeFields.oldUserTimeOffset); - - const timeChange = this.cfg.builder.makeDeviceEventTimeChange() - .with_change({ - from: sundial.formatDeviceTime(oldDateTime), - to: sundial.formatDeviceTime(record.jsDate), - agent: 'manual', - }) - .with_deviceTime(sundial.formatDeviceTime(record.jsDate)) - .set('index', record.headerFields.recordNumber) - .set('jsDate', record.jsDate); - this.postRecords.push(timeChange); - }); - } - - static addOutOfRangeAnnotation(recordBuilder, low, high, step, type) { - if (low !== null && recordBuilder.value < low + step) { - recordBuilder.with_value(low); - annotate.annotateEvent(recordBuilder, { - code: `${type}/out-of-range`, - value: 'low', - threshold: low + step, - }); - } else if (high !== null && recordBuilder.value > high - step) { - recordBuilder.with_value(high); - annotate.annotateEvent(recordBuilder, { - code: `${type}/out-of-range`, - value: 'high', - threshold: high - step, - }); - } - } - - buildCBGRecords() { - this.records.filter(elem => elem.headerFields.recordType === DB_RECORD_TYPE.HISTORICAL_DATA) - .forEach((record) => { - const cbg = this.cfg.builder.makeCBG() - .with_value(record.historyFields.glucoseValue) - .with_units('mg/dL') // values are always in 'mg/dL', independent of the unitOfMeasure setting - .with_deviceTime(sundial.formatDeviceTime(record.jsDate)) - .set('index', record.headerFields.recordNumber); - - this.constructor.addOutOfRangeAnnotation(cbg, GLUCOSE_LO, GLUCOSE_HI, 1, 'bg'); - - this.cfg.tzoUtil.fillInUTCInfo(cbg, record.jsDate); - this.postRecords.push(cbg.done()); - }); - } - - buildMeasurementRecords() { - this.records.filter(elem => - [DB_RECORD_TYPE.GLUCOSE_KETONE_SERVING, - DB_RECORD_TYPE.GLUCOSE_KETONE_MEAL, - DB_RECORD_TYPE.GLUCOSE_KETONE_CARBS, - ].includes(elem.headerFields.recordType)).forEach((record) => { - let recordBuilder; - - if (record.measurementFields.resultType === RESULT_VALUE_TYPE.GLUCOSE) { - recordBuilder = this.cfg.builder.makeSMBG() - .with_value(record.measurementFields.resultValue) - .with_units('mg/dL'); // values are always in 'mg/dL', independent of the unitOfMeasure setting - - this.constructor.addOutOfRangeAnnotation(recordBuilder, GLUCOSE_LO, GLUCOSE_HI, 1, 'bg'); - } else if (record.measurementFields.resultType === RESULT_VALUE_TYPE.KETONE) { - recordBuilder = this.cfg.builder.makeBloodKetone() - .with_value(record.measurementFields.resultValue / KETONE_VALUE_FACTOR) - .with_units('mmol/L'); - - this.constructor.addOutOfRangeAnnotation(recordBuilder, KETONE_LO, KETONE_HI, 1 / KETONE_VALUE_FACTOR, 'ketone'); - } - - if (recordBuilder) { - recordBuilder = recordBuilder.with_deviceTime(sundial.formatDeviceTime(record.jsDate)) - .set('index', record.headerFields.recordNumber); - this.cfg.tzoUtil.fillInUTCInfo(recordBuilder, record.jsDate); - this.postRecords.push(recordBuilder.done()); - } - }); - } - - handleCompressedDatabase(aapPacket) { - let decompressedBuffer = new Buffer(1); - let compressedOffset = 0; - - // copy table ID - decompressedBuffer[0] = aapPacket.data[compressedOffset]; - compressedOffset += 1; - - while (compressedOffset < aapPacket.dataLength) { - const blockType = aapPacket.data[compressedOffset]; - compressedOffset += 1; - - // parse 24 bit little endian block length - /* eslint-disable no-bitwise */ - let blockLength = aapPacket.data[compressedOffset] - | (aapPacket.data[compressedOffset + 1] << 8) - | (aapPacket.data[compressedOffset + 2] << 16); - /* eslint-enable no-bitwise */ - compressedOffset += 3; - - blockLength *= 4; // convert number of uint32 values to number of uint8 values - - if (blockType === COMPRESSION_TYPE.UNCOMPRESSED) { - decompressedBuffer = Buffer.concat([decompressedBuffer, - aapPacket.data.slice(compressedOffset, compressedOffset + blockLength)]); - compressedOffset += blockLength; - } else if (blockType === COMPRESSION_TYPE.ZERO_COMPRESSED) { - decompressedBuffer = Buffer.concat([decompressedBuffer, Buffer.alloc(blockLength)]); - compressedOffset += blockLength; - } else { - debug('handleCompressedDatabase: failed to decompress!'); - return; - } - } - - // build decompressed AAP packet to process - const decompressedAapPacket = { - packetLength: (aapPacket.packetLength - aapPacket.dataLength) + aapPacket.data.length, - data: decompressedBuffer, - dataLength: aapPacket.data.length, - opCode: OP_CODE.GET_DATABASE, - }; - - this.handleDatabase(decompressedAapPacket); - } - - /* eslint-disable no-bitwise */ - handleDatabase(aapPacket) { - if (aapPacket.dataLength === 0) { - return; - } - - let offset = 0; - const databaseTableId = aapPacket.data[offset]; - offset += 1; - - const headerFields = struct.unpack(aapPacket.data, offset, FORMAT.RECORD_HEADER, - ['recordNumber', 'recordType', 'isTimeValid', 'readerTime', 'userTimeOffset']); - headerFields.isTimeValid = ((headerFields.isTimeValid & 0x80) > 0); - offset += FORMAT_LENGTH.RECORD_HEADER; - - // calculate 32bit record number from 16bit header record number and next wrap around number - headerFields.recordNumber = - this.dbRecordNumberNextWrap[databaseTableId] - (0x10000 - headerFields.recordNumber); - - // find the lowest record number in the results database - if (databaseTableId === DB_TABLE_ID.GLUCOSE_RESULT) { - this.numResultRecords += 1; - this.oldestResultRecordNumber = - Math.min(this.oldestResultRecordNumber, headerFields.recordNumber); - } - - const dateTime = this.getDateTime(headerFields.readerTime, headerFields.userTimeOffset); - - if (headerFields.recordType === DB_RECORD_TYPE.TIME_CHANGE_RESULT) { - const timeChangeFields = struct.unpack(aapPacket.data, offset, FORMAT.TIME_CHANGE, - ['oldReaderTime', 'oldUserTimeOffset', 'valid', 'unused', 'CRC16']); - - // TODO: validate CRC16 - if (timeChangeFields.valid) { - this.records.push({ headerFields, timeChangeFields, jsDate: dateTime }); - } - } else if (headerFields.recordType === DB_RECORD_TYPE.HISTORICAL_DATA) { - const historyFields = struct.unpack(aapPacket.data, offset, FORMAT.HISTORICAL_DATA, - ['glucoseValue', 'lifeCounter', 'dataQualityErrorFlags', 'CRC16']); - historyFields.firstFlag = ((historyFields.glucoseValue & 0x1000) > 0); - historyFields.timeChangeFlag = ((historyFields.glucoseValue & 0x2000) > 0); - historyFields.foodFlag = ((historyFields.glucoseValue & 0x4000) > 0); - historyFields.rapidActingInsulinFlag = ((historyFields.glucoseValue & 0x8000) > 0); - historyFields.glucoseValue &= 0x03ff; - - // TODO: validate CRC16 - if (historyFields.dataQualityErrorFlags === 0) { - // debug('handleDatabase: historyFields:', historyFields, aapPacket.data.toString('hex')); - this.records.push({ headerFields, historyFields, jsDate: dateTime }); - } - } else if ([DB_RECORD_TYPE.GLUCOSE_KETONE_SERVING, - DB_RECORD_TYPE.GLUCOSE_KETONE_MEAL, - DB_RECORD_TYPE.GLUCOSE_KETONE_CARBS, - ].includes(headerFields.recordType)) { - const measurementFields = {}; - const RESULT_VALUE_OFFSET = 0; - struct.unpack(aapPacket.data, offset + RESULT_VALUE_OFFSET, 's', ['resultValue'], measurementFields); - measurementFields.resultType = (measurementFields.resultValue >> 14) & 0x3; - measurementFields.resultValue &= 0x03ff; - - const DATA_QUALITY_ERROR_FLAGS_OFFSET = 10; - struct.unpack(aapPacket.data, offset + DATA_QUALITY_ERROR_FLAGS_OFFSET, 's', ['dataQualityErrorFlags'], measurementFields); - - // TODO: validate CRC16 - if (measurementFields.dataQualityErrorFlags === 0) { - this.records.push({ headerFields, measurementFields, jsDate: dateTime }); - } - } else if (headerFields.recordType in DB_WRAP_RECORDS) { - const DB_RECORD_NUMBER_OFFSET = 0; - // TODO: validate CRC16 - this.dbRecordNumberNextWrap[databaseTableId] = - aapPacket.data.readUInt32LE(offset + DB_RECORD_NUMBER_OFFSET); - } - } - /* eslint-enable no-bitwise */ - - // eslint-disable-next-line no-unused-vars,class-methods-use-this - handleConfigSchema(aapPacket) { - // ignored, since they are currently hardcoded based on the specs - } - - handleConfigData(aapPacket) { - let offset = 0; - const tableId = aapPacket.data[offset]; - offset += 1; - if (tableId === CFG_TABLE_ID.METER_FACTORY_CONFIGURATION) { - const UNIT_OF_MEASURE_OFFSET = 133; - struct.unpack(aapPacket.data, offset + UNIT_OF_MEASURE_OFFSET, 'b', ['unitOfMeasure'], this.factoryConfig); - this.factoryConfig.unitOfMeasure = ['mmol/L', 'mg/dL'][this.factoryConfig.unitOfMeasure]; - - const TIME_CONVERSION_OFFSET = 156; - struct.unpack(aapPacket.data, offset + TIME_CONVERSION_OFFSET, 'i', ['timeConversion'], this.factoryConfig); - } - } -} diff --git a/lib/drivers/abbott/freeStyleLibreProtocol.js b/lib/drivers/abbott/freeStyleLibreProtocol.js deleted file mode 100644 index 8d2c3d56a0..0000000000 --- a/lib/drivers/abbott/freeStyleLibreProtocol.js +++ /dev/null @@ -1,667 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2017, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -/* - * *** FreeStyle Libre communication via USB HID *** - * - * *** HID DATA TRANSFER *** - * - HID reports are used to encapsulate the text and the binary protocol - * - HID report frames always have 64 bytes - * - * HID_FRAME: - * |--------------------------------------------------------------------- - * HID_HEADER HID_DATA - * |- |- |------------------------------------------------ - * COMMAND DATA_LENGTH PAYLOAD_DATA GARBAGE - * |- |- |------------------- |-------------------------- - * 0xnn 0xll (up to 62 bytes) - * - * - * HID_HEADER: - * - COMMAND: 1 byte - * - DATA_LENGTH: 1 byte (excluding HID_HEADER, so valid range is 0-62) - * - * HID_DATA: - * - PAYLOAD_DATA: DATA_LENGTH bytes actual data - * - GARBAGE: (62 - DATA_LENGTH) bytes fill the rest of the frame - * - * COMMAND CODES: - * - from host to device: - * --- 0x01: used in init? - * --- 0x04: used in init? - * --- 0x05: used in init? - * --- 0x0a: BINARY PROTOCOL request data with AAP OP_CODES - * --- 0x0d: ACK received packets - * --- 0x15: used in init? - * --- 0x21: TEXT PROTOCOL command - * --- 0x60: TEXT PROTOCOL command - * - * - from device to host: - * --- 0x06: answer to init 0x05, data in text format containing the device's serial number - * --- 0x0b: BINARY PROTOCOL response to 0x0a - * --- 0x0c: ACK received packets - * --- 0x22: at seemingly random points in the communication? always the same single data byte: 0x05 - * --- 0x34: answer to init 0x04, single data byte with different values? - * --- 0x35: answer to init 0x05, data in text format containing the devices software version - * --- 0x60: TEXT PROTOCOL response - * --- 0x71: answer to init 0x01, single data byte with value 0x01? - * - * - * *** BINARY PROTOCOL *** - * - * - ABMP: ADC Binary 22175 communication Meter Protocol - * --- ATP: ABMP Transport Protocol - * ----- AAP: ABMP Application Protocol - * - * ATP_FRAME: - * - * PAYLOAD_DATA - * |--------------------------------------------------------------- - * ATP_HEADER ATP_DATA - * |- |- |---- |--------------------- |---------- - * SEQ_RX SEQ_TX CRC AAP_FRAME_1 AAP_FRAME_2... - * |--------------------- - * AAP_HEADER AAP_DATA - * |--- |- |--------- - * AAP_LENGTH OP_CODE AAP_DATA - * - * ATP_FRAME: - * - ATP_HEADER - * --- SEQ_RX: 1 byte sequence information: the next expected packet's sequence number - * --- SEQ_TX: 1 byte sequence information: this packet's sequence number - * --- CRC: 32 bit CRC (variation of CRC32-CCITT, using 8 XORs but only a size 16 lookup table) - * - ATP_DATA - * --- can contain one or multiple AAP frames or only parts of one - * - * AAP_FRAME: - * - AAP_HEADER: - * --- AAP_LENGTH: 0 to 3 bytes - * ----- if high bit is set, the lower 7 bits are the length -> max 21 bits for length - * ----- if not, this byte is already the OP_CODE byte - * --- OP_CODE: 1 byte (high bit is 0) - * - AAP_DATA: up to 2MB spread over multiple ATP_DATA fields of consecutive ATP_FRAMES - * - * OP_CODE: (in addition to the ones defined in the specs) - * - 0x7d: make device flush its output buffer - * (sends remaining AAP data, even if not a full ATP frame can be filled) - * - * BINARY COMMAND: - * - exactly one AAP frame (contained in a single ATP frame, inside a single HID report frame) - * - * BINARY RESPONSE: - * - can span multiple AAP frames, possibly spread over multiple ATP and HID frames - * - only completely filled ATP frames are sent - * --- send OP Code 0x7d to force device to send remaining AAP data in partially filled ATP frame - * - * - * *** TEXT PROTOCOL *** - * - * TEXT COMMAND: - * - * PAYLOAD_DATA - * |---------------- - * MESSAGE SEP - * |-------- |-- - * $command? \r\n - * - * TEXT RESPONSE: - * - can span multiple HID frames, ending with STATUS ("CMD OK\r\n" or "CMD Fail!\r\n") - * - lines are separated by SEP: "\r\n" (0x0a 0x0d) - * - MESSAGE is followed by CHECKSUM and STATUS, each in its own line - * - * PAYLOAD_DATA - * |------------------------------------------------------------------------ - * MESSAGE SEP CHECKSUM SEP STATUS SEP - * |-------- |-- |------------- |-- |------ |-- - * message... \r\n "CKSM:[0-9A-F]{8}" \r\n "CMD OK" or "CMD Fail!" \r\n - * - */ - -import async from 'async'; -import structJs from '../../struct'; - -import { DEVICE_MODEL_NAME, OP_CODE, COMMAND, CRC32_TABLE } from './freeStyleLibreConstants'; - -const struct = structJs(); - -const isBrowser = typeof window !== 'undefined'; -// eslint-disable-next-line no-console -const debug = isBrowser ? require('bows')('FreeStyleLibreDriver') : console.log; - -const HID_PACKET_SIZE = 64; -const HID_HEADER_FORMAT = 'bb'; -const HID_HEADER_LENGTH = struct.structlen(HID_HEADER_FORMAT); - -const ATP_HEADER_FORMAT = 'bbi'; -const ATP_HEADER_LENGTH = struct.structlen(ATP_HEADER_FORMAT); - -const ACK_HEADER_FORMAT = 'bb2Z'; -const ACK_HEADER_LENGTH = struct.structlen(ACK_HEADER_FORMAT); - -const READ_TIMEOUT = 5000; // [ms] -const ACK_INTERVAL = 10; // [ms] - -const INIT_ACK_MAGIC_DATA = '\x00\x02'; - -// to know then all AAP responses for a request were received, -// we can look for the AAP data length of the final packet -const FINAL_AAP_DATA_LENGTHS = {}; -// contrary to the specs the db tables are concluded by just 0x31 with length 0, not 0x81 0x31 0xcc -FINAL_AAP_DATA_LENGTHS[OP_CODE.GET_DATABASE] = 0; -FINAL_AAP_DATA_LENGTHS[OP_CODE.GET_DB_SCHEMA] = 0; -FINAL_AAP_DATA_LENGTHS[OP_CODE.GET_DATE_TIME] = 8; -FINAL_AAP_DATA_LENGTHS[OP_CODE.GET_CFG_DATA] = -1; -FINAL_AAP_DATA_LENGTHS[OP_CODE.GET_CFG_SCHEMA] = 0; -FINAL_AAP_DATA_LENGTHS[OP_CODE.SET_COMPRESSION] = 1; - -// -// regular expressions for matching text protocol responses -// -const TEXT_CHECKSUM_FORMAT = 'CKSM:([0-9A-F]{8})\r\n'; -const TEXT_STATUS_FORMAT = 'CMD (OK|Fail!)\r\n'; -const TEXT_STATUS_REGEX = new RegExp(TEXT_STATUS_FORMAT); -// in javascript RegExp "[^]*" has to be used instead of ".*" to match all chars over multiple lines -// the multiline flag /s/ does not exist, so ".*" will never match newlines -const TEXT_RESPONSE_REGEX = new RegExp(`^([^]*)${TEXT_CHECKSUM_FORMAT}${TEXT_STATUS_FORMAT}`); - -const DB_RECORD_NUMBER_REGEX = new RegExp('^DB Record Number = ([0-9]+)$'); - -// after db record responses (e.g. $arresult?, $history?) an additional line is send, -// containing the record count and an additional checksum of just the records: -// COUNT,RECORD_CHECKSUM\r\n -// eslint-disable-next-line no-control-regex -const DB_RECORDS_REGEX = new RegExp('^([^]*\r\n)([0-9]+),([0-9A-F]{8})$'); - - -export default class FreeStyleLibreProtocol { - constructor(config) { - this.config = config; - this.hidDevice = config.deviceComms; - this.sequenceRx = 0; - this.sequenceTx = 0; - this.lastAckRx = 0; - } - - readResponse(responseType, timeout, cb) { - let receiveTimeout = null; - const resetReceiveTimeout = () => { - if (receiveTimeout !== null) { - clearTimeout(receiveTimeout); - } - receiveTimeout = setTimeout(() => { - debug('readResponse: TIMEOUT'); - const e = new Error('Timeout error.'); - e.name = 'TIMEOUT'; - return cb(e, null); - }, timeout); - }; - - let ackInterval = null; - if (responseType === COMMAND.BINARY_RESPONSE) { - ackInterval = setInterval(() => { - if (this.sequenceRx !== this.lastAckRx) { - this.sendAck(); - } - }, ACK_INTERVAL); - } - - const aapPackets = []; - let receiveBuffer = new Buffer(0); - async.doWhilst( - (callback) => { - resetReceiveTimeout(); - this.hidDevice.receive((buffer) => { - if (buffer !== undefined && buffer.length > 0) { - const hidPacket = this.constructor.parseHidPacket(buffer); - - if (hidPacket.responseType === COMMAND.TEXT_RESPONSE) { - // append newly received data to message string - receiveBuffer = Buffer.concat([receiveBuffer, hidPacket.data]); - } else if (hidPacket.responseType === COMMAND.BINARY_RESPONSE) { - const atpPacket = this.constructor.parseAtpFrame(hidPacket.data); - if (atpPacket === null) { - // CRC error: try to get damaged package again - this.sendAck(); - } else if (this.sequenceRx === atpPacket.sequenceTx) { - // next sequence number expected is one higher than the one just received - this.sequenceRx = (this.sequenceRx + 1) % 0x100; - - receiveBuffer = Buffer.concat([receiveBuffer, atpPacket.data]); - } else { - debug('readResponse: Received wrong sequence number', atpPacket.sequenceTx, - ', expected', this.sequenceRx); - // try to get missing package by sending an ACK containing the next expected - // sequence number to the device - this.sendAck(); - } - } else if (hidPacket.responseType === COMMAND.ACK_FROM_DEVICE) { - - // TODO: handle device ack packets - // either throw error on unexpected sequence number - // or actually cache sent packets and resend starting from requested number - - } - } - - // run test to see if we are done receiving - return callback(); - }); - }, - - () => { - if (responseType === COMMAND.TEXT_RESPONSE) { - // if we are waiting for a text response and the buffer contains the status line: done - return !(TEXT_STATUS_REGEX.exec(receiveBuffer.toString())); - } else if (responseType === COMMAND.BINARY_RESPONSE) { - // collect all valid AAP packets from the buffer - let aapPacket; - do { - aapPacket = this.constructor.parseAapFrame(receiveBuffer); - if (aapPacket !== null) { - aapPackets.push(aapPacket); - // remove parsed packet from buffer - receiveBuffer = receiveBuffer.slice(aapPacket.packetLength); - } - } while (aapPacket !== null && receiveBuffer.length > 0); - - if (aapPackets.length === 0) { - return true; - } - - // we can recognise the last AAP packet of a response from its data length - // or from the number of AAP packets - const lastAapPacket = aapPackets[aapPackets.length - 1]; - const finalLength = FINAL_AAP_DATA_LENGTHS[lastAapPacket.opCode]; - // if the finalLength value is negative it describes the index of the final AAP packets - if (finalLength < 0) { - return aapPackets.length < -finalLength; - } - // if it is 0 or higher, it describes the dataLength of the final AAP packet - return lastAapPacket.dataLength !== finalLength; - } else if (responseType === null) { - // no response needed - return false; - } - // continue iterating - return true; - }, - - (err) => { - clearTimeout(receiveTimeout); - // send a final Ack and stop interval timer - if (ackInterval !== null) { - clearInterval(ackInterval); - this.sendAck(); - } - if (err) { - return cb(err, null); - } - if (responseType === COMMAND.BINARY_RESPONSE) { - return cb(null, aapPackets); - } - return cb(null, receiveBuffer.toString()); - }, - ); - } - - static validateTextChecksum(dataString, expectedChecksum) { - /* eslint-disable no-bitwise */ - const calculatedChecksum = dataString.split('') - .reduce((a, b) => a + (b.charCodeAt(0) & 0xff), 0); - /* eslint-enable no-bitwise */ - if (calculatedChecksum !== expectedChecksum) { - debug(`validateTextChecksum: wrong checksum: ${calculatedChecksum} != ${expectedChecksum}`); - } - return calculatedChecksum === expectedChecksum; - } - - static parseHidPacket(buffer) { - const packet = struct.unpack(buffer, 0, HID_HEADER_FORMAT, ['responseType', 'dataLen']); - packet.data = buffer.slice(HID_HEADER_LENGTH, HID_HEADER_LENGTH + packet.dataLen); - return packet; - } - - static buildHidPacket(commandType, data) { - const bytes = new Uint8Array(HID_PACKET_SIZE); - const counter = struct.pack(bytes, 0, HID_HEADER_FORMAT, commandType, data.length); - if (data.length) { - // data can be either a TypedArray or a string - let dataTypeChar = 'B'; - if (typeof (data) === 'string') { - dataTypeChar = 'Z'; - } - struct.pack(bytes, counter, data.length + dataTypeChar, data); - } - return bytes.buffer; - } - - /* eslint-disable no-bitwise */ - static parseAapFrame(buffer) { - const packet = { - dataLength: 0, - }; - - let dataLengthNumBytes = 0; - // the first 0 to 3 bytes describe the aap frame length in their lower 7 bits in little endian - for (let i = 0; i <= 2; i++) { - if (i >= buffer.length) { - return null; - } - const values = struct.unpack(buffer, i, 'b', ['byte']); - // if highest bit is not set, this is already the command byte - if ((values.byte & 0x80) === 0) { - break; - } - // highest bit was set, extract lower 7 bits as length value - let lengthValue = values.byte & 0x7f; - // shift these 7 bits to the left depending on the index i - lengthValue <<= (7 * i); - // combine these bits with the previous length value - packet.dataLength |= lengthValue; - - dataLengthNumBytes += 1; - } - - const opCodeNumBytes = 1; - - if (buffer.length > dataLengthNumBytes) { - // add opCode to packet - struct.unpack(buffer, dataLengthNumBytes, 'b', ['opCode'], packet); - } - - // if there is data missing, return null - packet.packetLength = dataLengthNumBytes + opCodeNumBytes + packet.dataLength; - const numBytesMissing = packet.packetLength - buffer.length; - if (numBytesMissing > 0) { - return null; - } - - if ((packet.opCode & 0x80) !== 0) { - debug(`parseAapFrame: Faulty op code: 0x${packet.opCode.toString(16)}`); - return null; - } - - // add data to packet - packet.data = buffer.slice(dataLengthNumBytes + opCodeNumBytes, packet.packetLength); - - return packet; - } - - static buildAapFrame(opCode, dataArrayOptional) { - let dataArray = dataArrayOptional; - if (dataArray === undefined) { - dataArray = []; - } - const aapDataLengthBytes = []; - let dataLength = dataArray.length; - // as long as there are length bits left - while (dataLength > 0) { - // put the lowest 7 bits in a length byte and set the high bit - const lengthByte = 0x80 | (dataLength & 0x7f); - // append new length byte to length string (little endian ordering) - aapDataLengthBytes.push(lengthByte); - // shift length by the 7 bits just used - dataLength >>= 7; - } - - const packetFormat = `${aapDataLengthBytes.length}Bb${dataArray.length}B`; - const packetLength = struct.structlen(packetFormat); - - const bytes = new Uint8Array(packetLength); - struct.pack(bytes, 0, packetFormat, aapDataLengthBytes, opCode, dataArray); - return bytes; - } - - static parseAtpFrame(buffer) { - const packet = struct.unpack(buffer, 0, ATP_HEADER_FORMAT, ['sequenceRx', 'sequenceTx', 'crc32']); - packet.data = buffer.slice(ATP_HEADER_LENGTH); - const crc32 = FreeStyleLibreProtocol.calcCrc32(packet.data); - if (crc32 !== packet.crc32) { - debug('parseAtpFrame: CRC32 did not match:', crc32.toString(16), '!=', packet.crc32.toString(16)); - return null; - } - return packet; - } - - static calcCrc32(buffer) { - // make zero-padded buffer with length that is multiple of 4 - const paddedBuffer = Buffer.alloc((buffer.length + 3) & 0xfffffffc); - Buffer.from(buffer).copy(paddedBuffer); - - let remainder = 0xffffffff; - for (let index = 0; index < paddedBuffer.length / 4; index++) { - let data = paddedBuffer.readUInt32LE(index * 4); - - data ^= remainder; - - for (let i = 0; i < 8; i++) { - remainder = data >>> 28; - data <<= 4; - data ^= CRC32_TABLE[remainder >>> 0]; // use unsigned remainder as index - } - - remainder = data; - } - return remainder >>> 0; // return unsigned remainder - } - /* eslint-enable no-bitwise */ - - buildAtpFrame(aapFrameArray) { - const atpCrc = this.constructor.calcCrc32(aapFrameArray); - const atpFrameArray = new Uint8Array(ATP_HEADER_LENGTH + aapFrameArray.length); - struct.pack(atpFrameArray, 0, `${ATP_HEADER_FORMAT + aapFrameArray.length}B`, - this.sequenceRx, - this.sequenceTx, - atpCrc, - aapFrameArray); - return atpFrameArray; - } - - buildAckFrame(unknownDataOptional) { - let unknownData = unknownDataOptional; - if (unknownData === undefined) { - unknownData = '\x00\x00'; - } - const ackFrameArray = new Uint8Array(ACK_HEADER_LENGTH); - struct.pack(ackFrameArray, 0, ACK_HEADER_FORMAT, - this.sequenceRx, - this.sequenceTx, - unknownData); - return ackFrameArray; - } - - sendAck(unknownData, cbOptional) { - let cb = cbOptional; - if (cb === undefined) { - cb = () => {}; - } - this.lastAckRx = this.sequenceRx; - const ackFrameArray = this.buildAckFrame(unknownData); - this.hidDevice.send(this.constructor.buildHidPacket(COMMAND.ACK_FROM_HOST, ackFrameArray), cb); - } - - sendCommand(command, responseType, data, cb) { - this.hidDevice.send(this.constructor.buildHidPacket(command, data), () => { - this.readResponse(responseType, READ_TIMEOUT, cb); - }); - } - - parseTextResponse(responseData) { - const match = TEXT_RESPONSE_REGEX.exec(responseData); - if (!match) { - return new Error('Invalid text responseData format.'); - } - const data = match[1]; - const checksum = parseInt(match[2], 16); - const result = match[3]; - - if (result === 'OK') { - if (this.constructor.validateTextChecksum(data, checksum)) { - return data.replace(/\r\n$/, ''); - } - return new Error('Invalid checksum.'); - } - return new Error(`Device responseData was not "OK", but "${result}"`); - } - - requestTextResponse(command, successCallback, errorCallback) { - debug(`requestTextResponse: Sending command: 0x${COMMAND.TEXT_REQUEST.toString(16)}`, - ', data: ', command); - this.sendCommand(COMMAND.TEXT_REQUEST, COMMAND.TEXT_RESPONSE, command, (err, responseData) => { - if (err) { - debug('requestTextResponse: error: ', err); - return errorCallback(err, responseData); - } - - const data = this.parseTextResponse(responseData); - if (data instanceof Error) { - return errorCallback(data, responseData); - } - debug(`requestTextResponse: data: "${data}"`); - return successCallback(data); - }); - } - - requestBinaryResponse(opCode, aapData, cb) { - const atpFrameArray = this.buildAtpFrame(this.constructor.buildAapFrame(opCode, aapData)); - this.sequenceTx = (this.sequenceTx + 1) % 0x100; - debug(`requestBinaryResponse: Sending command: 0x${COMMAND.BINARY_REQUEST.toString(16)}`, - ', data: ', Buffer.from(atpFrameArray).toString('hex')); - this.hidDevice.send( - this.constructor.buildHidPacket(COMMAND.BINARY_REQUEST, atpFrameArray), () => { - const flushData = this.buildAtpFrame(this.constructor.buildAapFrame(OP_CODE.FLUSH_BUFFERS)); - this.sequenceTx = (this.sequenceTx + 1) % 0x100; - this.sendCommand(COMMAND.BINARY_REQUEST, COMMAND.BINARY_RESPONSE, flushData, - (err, aapPackets) => { - if (err) { - debug('requestBinaryResponse: error: ', err); - return cb(err, aapPackets); - } - - debug(`requestBinaryResponse: num aapPackets: ${aapPackets.length}`); - return cb(null, aapPackets); - }); - }); - } - - getDBRecords(command, successCallback, errorCallback) { - debug(`getDBRecords: ${command}`); - this.requestTextResponse(command, (data) => { - const match = DB_RECORDS_REGEX.exec(data); - if (!match) { - return errorCallback(new Error('Invalid response format for database records.'), data); - } - let dbRecords = match[1]; - const dbRecordCount = parseInt(match[2], 10); - const dbRecordChecksum = parseInt(match[3], 16); - - if (!this.constructor.validateTextChecksum(dbRecords, dbRecordChecksum)) { - return errorCallback(new Error('Invalid database record checksum.'), data); - } - - dbRecords = dbRecords.split('\r\n'); - - if (dbRecordCount !== dbRecords.length) { - return errorCallback( - new Error(`Invalid database record count: ${dbRecordCount} != ${dbRecords.length}`), data); - } - - return successCallback(null, dbRecords); - }, errorCallback); - } - - // this is currently unused as it uses part of the text protocol not meant for production - getReaderResultData(cb) { - this.getDBRecords('$arresult?', (data) => { - cb(null, { readerResultData: data }); - }, cb); - } - - // this is currently unused as it uses part of the text protocol not meant for production - getHistoricalScanData(cb) { - this.getDBRecords('$history?', (data) => { - cb(null, { historicalScanData: data }); - }, cb); - } - - getDBRecordNumber(cb) { - const command = '$dbrnum?'; - this.requestTextResponse(command, (data) => { - const match = DB_RECORD_NUMBER_REGEX.exec(data); - if (!match) { - return cb(new Error('Invalid response format for database record number.'), data); - } - const dbRecordNumber = parseInt(match[1], 10); - - return cb(null, { dbRecordNumber }); - }, cb); - } - - setCompression(enableCompression, cb) { - this.requestBinaryResponse(OP_CODE.SET_COMPRESSION, [enableCompression], cb); - } - - getDateTime(cb) { - this.requestBinaryResponse(OP_CODE.GET_DATE_TIME, [], cb); - } - - getDbSchema(cb) { - this.requestBinaryResponse(OP_CODE.GET_DB_SCHEMA, [], cb); - } - - getCfgSchema(cb) { - this.requestBinaryResponse(OP_CODE.GET_CFG_SCHEMA, [], cb); - } - - getCfgData(tableNumber, cb) { - this.requestBinaryResponse(OP_CODE.GET_CFG_DATA, [tableNumber], cb); - } - - getDatabase(tableNumber, cb) { - this.requestBinaryResponse(OP_CODE.GET_DATABASE, [tableNumber], cb); - } - - getFirmwareVersion(cb) { - this.requestTextResponse('$swver?', (data) => { - cb(null, { firmwareVersion: data }); - }, cb); - } - - getSerialNumber(cb) { - this.requestTextResponse('$sn?', (data) => { - cb(null, { serialNumber: data }); - }, cb); - } - - initCommunication(cb) { - const initFunctions = [ - (callback) => { this.sendCommand(COMMAND.INIT_REQUEST_1, null, '', callback); }, - (callback) => { this.sendAck(INIT_ACK_MAGIC_DATA, callback); }, - (callback) => { this.sendCommand(COMMAND.INIT_REQUEST_2, null, '', callback); }, - (callback) => { this.sendCommand(COMMAND.INIT_REQUEST_3, null, '', callback); }, - (callback) => { this.sendCommand(COMMAND.INIT_REQUEST_4, null, '', callback); }, - ]; - async.series(initFunctions, (err, result) => { - cb(err, result); - }); - } - - static probe(cb) { - debug(`probe: not using probe for ${DEVICE_MODEL_NAME}`); - cb(); - } -} diff --git a/lib/drivers/abbott/tools/README b/lib/drivers/abbott/tools/README deleted file mode 100644 index 2a8a50cce5..0000000000 --- a/lib/drivers/abbott/tools/README +++ /dev/null @@ -1,4 +0,0 @@ -To analyze the USB protocol of the Abbott FreeStyle Libre, install this Lua plugin for Wireshark and sniff the traffic on the appropriate usbmon interface. - -To install this plugin, copy it or make a symlink to it in: -~/.wireshark/plugins diff --git a/lib/drivers/abbott/tools/fslibre_usb_dissector.lua b/lib/drivers/abbott/tools/fslibre_usb_dissector.lua deleted file mode 100644 index cf36cf8727..0000000000 --- a/lib/drivers/abbott/tools/fslibre_usb_dissector.lua +++ /dev/null @@ -1,278 +0,0 @@ --- --- Wireshark plugin to dissect the USB HID packets of the Abbott FreeStyle Libre --- - --- needed for debugging this script ---_G.debug = require("debug") ---require("mobdebug").start() --- - -local fslibre_usb = Proto("fslibre_usb", "Abbott FreeStyle Libre USB Protocol") - -local fslibre_dump = ProtoField.new("FSLibre Dump", "fslibre_usb.dump", ftypes.BYTES) - -local command = ProtoField.new("Command", "fslibre_usb.command", ftypes.UINT8, nil, base.HEX) -local data_length = ProtoField.new("Data Length", "fslibre_usb.data_length", ftypes.UINT8) - -local text = ProtoField.new("Text", "fslibre_usb.text", ftypes.STRING) - -local atp_frame = ProtoField.new("ATP Frame", "fslibre_usb.atp", ftypes.NONE) -local atp_data = ProtoField.new("ATP Data", "fslibre_usb.atp.data", ftypes.BYTES) -local atp_sequence_rx = ProtoField.new("ATP Seq Rx", "fslibre_usb.atp.sequence_rx", ftypes.UINT8) -local atp_sequence_tx = ProtoField.new("ATP Seq Tx", "fslibre_usb.atp.sequence_tx", ftypes.UINT8) -local atp_crc32 = ProtoField.new("ATP CRC32", "fslibre_usb.atp.crc32", ftypes.UINT32, nil, base.HEX) -local atp_crc32bin = ProtoField.new("ATP CRC32 BIN", "fslibre_usb.atp.crc32bin", ftypes.STRING) -local atp_unknown = ProtoField.new("ATP UNKNOWN", "fslibre_usb.atp.unknown", ftypes.UINT16, nil, base.HEX) - -local aap_frame = ProtoField.new("AAP Frame", "fslibre_usb.aap", ftypes.STRING) -local aap_data_length = ProtoField.new("AAP Data Length", "fslibre_usb.aap.data_length", ftypes.UINT32) -local aap_op_code = ProtoField.new("AAP OP Code", "fslibre_usb.aap.op_code", ftypes.UINT8, nil, base.HEX) -local aap_data = ProtoField.new("AAP Data", "fslibre_usb.aap.data", ftypes.BYTES) -local aap_dump = ProtoField.new("AAP Dump", "fslibre_usb.aap.dump", ftypes.BYTES) - - -fslibre_usb.fields = { - fslibre_dump, - command, - text, - atp_frame, - data_length, - atp_data, - atp_sequence_rx, - atp_sequence_tx, - atp_crc32, - atp_crc32bin, - atp_unknown, - aap_frame, - aap_data_length, - aap_op_code, - aap_data, - aap_dump -} - -local HID_REPORT_LENGTH = 64 -local AAP_OPCODE_LENGTH = 1 - -local pkt_state = {} -local partial_aap_buf = {} - -local function to_bits(num, bits) - -- returns a string of bits, most significant first - bits = bits or math.max(1, select(2, math.frexp(num))) - local t = {} -- table containing the bits - for b = bits, 1, -1 do - t[b] = math.fmod(num, 2) - num = math.floor((num - t[b]) / 2) - end - return table.concat(t) -- convert table to string -end - - -local function dissect_aap(atp_payload_buf, atp_tree) - local aap_data_length_num_bytes = 0 - local aap_data_length_value = 0 - -- the first 0 to 3 bytes describe the aap frame length in their lower 7 bits in little endian - for i = 0, 2 do - -- if we are still parsing the length when we reach the buffer's end, we need at least 1 more byte - if i >= atp_payload_buf:len() then - -- show data as partial aap frame - atp_tree:add(aap_frame, atp_payload_buf:range(0), "[partial]: length not known yet") - return -1 - end - - local byte_value = atp_payload_buf:range(i, 1):uint() - -- if highest bit is not set, this is already the command byte - if not bit32.btest(byte_value, 0x80) then - break - end - -- highest bit was set, extract lower 7 bits as length value - local byte_length_value = bit32.band(byte_value, 0x7f) - -- shift these 7 bits to the left depending on the index i - byte_length_value = bit32.lshift(byte_length_value, 7 * i) - -- combine these bits with the previous length value - aap_data_length_value = bit32.bor(byte_length_value, aap_data_length_value) - aap_data_length_num_bytes = aap_data_length_num_bytes + 1 - end - - local aap_data_offset = aap_data_length_num_bytes + AAP_OPCODE_LENGTH - local aap_data_length_in_this_frame = math.min(atp_payload_buf:len() - aap_data_offset, aap_data_length_value) - - -- if aap packet is longer than current buffer, return number of missing bytes as negative number - if aap_data_length_in_this_frame < aap_data_length_value then - -- show data as partial aap frame, with info about missing bytes and possibly opcode - local op_code_info = "" - if atp_payload_buf:len() > aap_data_length_num_bytes then - local aap_op_code_value = atp_payload_buf:range(aap_data_length_num_bytes, 1):uint() - op_code_info = string.format("[OP Code 0x%x]", aap_op_code_value) - end - local description = string.format("[partial]%s %d of %d bytes", - op_code_info, - aap_data_offset + aap_data_length_in_this_frame, - aap_data_offset + aap_data_length_value) - atp_tree:add(aap_frame, atp_payload_buf:range(0), description) - - return - (aap_data_length_value - aap_data_length_in_this_frame) - end - - -- check that opcode does not have the highest bit set, otherwise cancel parsing due to faulty data - local aap_op_code_value = atp_payload_buf:range(aap_data_length_num_bytes, 1):uint() - if bit32.btest(aap_op_code_value, 0x80) then - return 0 - end - - -- add new aap sub-tree - local aap_tree = atp_tree:add(aap_frame, - atp_payload_buf:range(0, aap_data_length_num_bytes + AAP_OPCODE_LENGTH + aap_data_length_in_this_frame), "") - - -- add hidden field that can be used in custom column to show aap packet data - aap_tree:add(aap_dump, atp_payload_buf:range(0, aap_data_offset + aap_data_length_in_this_frame)):set_hidden() - - -- mark the aap data length bytes at the aap frame start - aap_tree:add(aap_data_length, atp_payload_buf:range(0, aap_data_length_num_bytes), aap_data_length_value) - - -- aap op code is the first byte after the aap length - aap_tree:add(aap_op_code, atp_payload_buf:range(aap_data_length_num_bytes, AAP_OPCODE_LENGTH)) - - if aap_data_length_in_this_frame > 0 then - -- aap data bytes start after the op code - aap_tree:add(aap_data, atp_payload_buf:range(aap_data_offset, aap_data_length_in_this_frame)) - end - - return aap_data_offset + aap_data_length_in_this_frame -end - - -local function reassemble_aap(atp_data_buf, pktinfo, atp_tree) - - local aap_buf - local state = pkt_state[pktinfo.number] - - if state ~= nil then - -- we've already processed this packet - aap_buf = ByteArray.tvb(state.buffer, "AAP Buffer") - state.processed = True - else - -- first time processing this packet - state = {} - - if partial_aap_buf[tostring(pktinfo.src)] ~= nil then - partial_aap_buf[tostring(pktinfo.src)]:append(atp_data_buf(0):bytes()) - aap_buf = ByteArray.tvb(partial_aap_buf[tostring(pktinfo.src)], "AAP Buffer") - else - aap_buf = atp_data_buf - end - end - - if state.processed == nil then - state.buffer = aap_buf(0):bytes() - end - - while aap_buf:len() > 0 do - local aap_packet_length = dissect_aap(aap_buf, atp_tree) - - if aap_packet_length == 0 then - - -- error disecting aap - if state.processed == nil then - pkt_state[pktinfo.number] = state - partial_aap_buf[tostring(pktinfo.src)] = nil - end - return - - elseif aap_packet_length < 0 then - - -- we don't have all the data we need yet - if state.processed == nil then - -- save remaining data - partial_aap_buf[tostring(pktinfo.src)] = aap_buf(0):bytes() - pkt_state[pktinfo.number] = state - end - return - - else - -- consumed one aap packet from aap_buf, remove it and continue parsing - aap_buf = aap_buf:range(aap_packet_length):tvb() - end - end - - if state.processed == nil then - -- emptied aap_buf without any remaining partial aap data - pkt_state[pktinfo.number] = state - partial_aap_buf[tostring(pktinfo.src)] = nil - end -end - - - -function fslibre_usb.dissector(tvbuf, pktinfo, root) - - pktinfo.cols.protocol:set("fslibre_usb") - local pktlen = tvbuf:len() - - if pktlen < HID_REPORT_LENGTH then - -- tell Wireshark that this packet was not for us - return 0 - end - - -- cut off leading data before the actual HID report, which is always 64 bytes - local hid_report_buf = tvbuf:range(pktlen - HID_REPORT_LENGTH, HID_REPORT_LENGTH):tvb() - - pktinfo.cols.protocol = fslibre_usb.name - - local command_value = hid_report_buf:range(0, 1):uint() - local data_length_value = hid_report_buf:range(1, 1):uint() - - local data_offset = 2 - local tree = root:add(fslibre_usb, hid_report_buf:range(0, data_offset + data_length_value)) - - -- add hidden field that can be used in custom column to show whole packet data - tree:add(fslibre_dump, hid_report_buf:range(0, data_offset + data_length_value)):set_hidden() - - -- actually the command is only in the lower 6 bits of the first byte, - -- but the 2 high bits are currently always 0 anyhow - tree:add(command, hid_report_buf:range(0, 1)) - - tree:add(data_length, hid_report_buf:range(1, 1)) - - if data_length_value > 0 then - if command_value == 0x60 or command_value == 0x21 or command_value == 0x06 or command_value == 0x35 then - tree:add(text, hid_report_buf:range(data_offset, data_length_value)) - - else - local atp_tree = tree:add(atp_frame, hid_report_buf:range(data_offset, data_length_value)) - - local atp_buf = hid_report_buf:range(data_offset, data_length_value):tvb() - atp_tree:add(atp_data, atp_buf:range()):set_hidden() - - if data_length_value >= 2 then - atp_tree:add(atp_sequence_rx, atp_buf:range(0, 1)) - atp_tree:add(atp_sequence_tx, atp_buf:range(1, 1)) - - if data_length_value >= 6 then - atp_tree:add(atp_crc32, atp_buf:range(2, 4)) - - local crc32_value = atp_buf:range(2, 4):uint() - atp_tree:add(atp_crc32bin, atp_buf:range(2, 4), to_bits(crc32_value, 32)):set_hidden() - - if data_length_value > 6 then - reassemble_aap(atp_buf:range(6):tvb(), pktinfo, atp_tree) - end - - elseif data_length_value == 4 then - atp_tree:add(atp_unknown, atp_buf:range(2, 2)) - end - end - end - end -end - - -function fslibre_usb.init() - -- needed to track the state of aap reassembly - pkt_state = {} - partial_aap_buf = {} - - -- register this disector for USB vendor:product 1a61:3650 - DissectorTable.get("usb.product"):add(0x1a613650, fslibre_usb) -end - diff --git a/lib/drivers/abbott/tools/lua_debug.sh b/lib/drivers/abbott/tools/lua_debug.sh deleted file mode 100755 index e7bec69a04..0000000000 --- a/lib/drivers/abbott/tools/lua_debug.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -# This script helps with debugging a Lua Wireshark plugin on Linux (and possibly OSX). -# Before using it, install Zero Brane Studio IDE and start its Debugger Server. -# Open the plugin script to be debugged in the IDE from its install path (e.g. the Wireshark plugin dir). -# Make sure the ZBS variable is set to the install path of the IDE: -export ZBS=/opt/zbstudio - -# In your Lua plugin add these two lines at the very top: -# _G.debug = require("debug") -# require("mobdebug").start() - -# Run this script and it will stop at the above lines in the debugger. - - - -if [[ "$(uname -m)" == "x86_64" ]]; then ARCH="x64"; else ARCH="x86"; fi - -export LUA_PATH="./?.lua;$ZBS/lualibs/?/?.lua;$ZBS/lualibs/?.lua" -export LUA_CPATH="$ZBS/bin/?.so;$ZBS/bin/linux/$ARCH/clibs52/?.so" - -if [ "$#" -ne 1 ]; then - echo "usage: $0 PCAP_FILE" - exit -fi - -# If the plugin is loaded automatically by Wireshark from one of its plugin directories: -tshark -r $1 -#wireshark -r $1 - -# If the plugin is to be manually loaded: -#tshark -X lua_script:fslibre_usb_dissector.lua -r $1 -#wireshark -X lua_script:fslibre_usb_dissector.lua -r $1 diff --git a/lib/struct.js b/lib/struct.js index 1ac5ae07e7..7042c07695 100644 --- a/lib/struct.js +++ b/lib/struct.js @@ -23,7 +23,6 @@ // H -- a 2-byte signed integer in big-endian format // z -- a zero-terminated string of maximum length controlled by the size parameter. // Z -- a string of bytes with the length controlled by the size parameter. -// B -- an array of bytes with the length controlled by the size parameter. // f -- a 4-byte float in little-endian format // F -- a 4-byte float in big-endian format // . -- the appropriate number of bytes is ignored (used for padding) @@ -62,9 +61,6 @@ var _ = require('lodash'); module.exports = function() { var extractString = function(bytes, start, len) { - if (start === undefined) { - start = 0; - } if (!len) { len = bytes.length; } @@ -208,13 +204,6 @@ module.exports = function() { var storeByte = function(v, b, st) { b[st] = v & 0xFF; }; - var storeBytes = function(v, b, st) { - var i=0; - while (i { - if (v !== null && typeof v === 'object' && 'type' in v && - v.type === 'Buffer' && 'data' in v && Array.isArray(v.data)) { - // re-create Buffer objects for data fields of aapPackets - return new Buffer(v.data); - } - return v; - }); -} - -describe('freeStyleLibreData.js', () => { - const cfg = { - builder: builder(), - timezone: 'Europe/Berlin' - }; - - describe('non-static', () => { - let fsLibreData; - - beforeEach(function(){ - fsLibreData = new FreeStyleLibreData(cfg); - }); - - describe('process AAP packets', () => { - - it('should correctly restore 32bit records numbers', () => { - const historicalRecordJson = ` - { - "dataLength": 21, - "opCode": 49, - "packetLength": 23, - "data": {"type":"Buffer","data":[6,164,144,12,128,83,91,16,5,220,205,255,255,75,0,224,31,0,0,69,248]} - } - `; - const data = { - aapPackets: [ - deserialize(factoryConfigJson), - deserialize(historicalRecordJson) - ] - }; - - // potentially problematic 32bit record numbers - const recordNumbers = [0, 1, 0xfffe, 0xffff, 0x10000, 0x10001, 0xffffffff]; - - recordNumbers.forEach(recordNumber => { - // use lower 16bits to replace 16bit record number in historical record - data.aapPackets[1].data[1] = recordNumber & 0xff; - data.aapPackets[1].data[2] = (recordNumber >> 8) & 0xff; - - // use recordNumber + 1 as current DB record number (which will be used for next record written) - const postRecords = fsLibreData.processAapPackets(data.aapPackets, recordNumber + 1); - - // check if valid record was returned and contains correct 32bit record number - expect(postRecords.length).equals(1); - expect(postRecords[0].index).equals(recordNumber); - }); - }); - - }); - - }); - - describe('static', () => { - - describe('validate annotations', () => { - it('should only annotate out-of-range BG values', () => { - const inputData = [ - [GLUCOSE_LO, true], - [GLUCOSE_HI, true], - [GLUCOSE_LO - 1, true], - [GLUCOSE_HI + 1, true], - [GLUCOSE_LO + 1, false], - [GLUCOSE_HI - 1, false], - ]; - inputData.forEach(([value, expectedResult]) => { - const cbg = cfg.builder.makeCBG() - .with_value(value) - .with_units('mg/dL'); - - FreeStyleLibreData.addOutOfRangeAnnotation(cbg, GLUCOSE_LO, GLUCOSE_HI, 1, 'bg'); - - const isAnnotated = annotate.isAnnotated(cbg, 'bg/out-of-range'); - - expect(isAnnotated).equals(expectedResult); - }); - }); - }); - - }); -}); diff --git a/test/lib/abbot/testFreeStyleLibreProtocol.js b/test/lib/abbot/testFreeStyleLibreProtocol.js deleted file mode 100644 index 8d05a72f74..0000000000 --- a/test/lib/abbot/testFreeStyleLibreProtocol.js +++ /dev/null @@ -1,133 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2017, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -/* global beforeEach, describe, it */ - -import {expect} from 'salinity'; - -import FreeStyleLibreProtocol from '../../../lib/drivers/abbott/freeStyleLibreProtocol'; - -describe('freeStyleLibreProtocol.js', () => { - const cfg = {}; - - describe('non-static', () => { - let protocol; - - beforeEach(function(){ - protocol = new FreeStyleLibreProtocol(cfg); - }); - - describe('parse text responses', () => { - - it('should parse and return valid text responses', () => { - const inputData = [ - ['DB Record Number = 226988\r\nCKSM:00000765\r\nCMD OK\r\n', 'DB Record Number = 226988'], - ['2.1.2\r\nCKSM:00000108\r\nCMD OK\r\n', '2.1.2'] - ]; - inputData.forEach(([data, expectedResult]) => { - const result = protocol.parseTextResponse(data); - expect(result).deep.equals(expectedResult); - } - ); - }); - - - it('should not accept invalid text responses', () => { - const inputData = [ - ['DB Record Number = 226988\r\nCKSM:00000765\r\nCMD Fail!\r\n', Error], - ['2.1.2\r\nCKSM:00000111\r\nCMD OK\r\n', Error], - ['2.1.2\r\nCKSM:108\r\nCMD OK\r\n', Error] - ]; - inputData.forEach(([data, expectedResult]) => { - const result = protocol.parseTextResponse(data); - expect(result).instanceof(expectedResult); - } - ); - }); - }); - - }); - - describe('static', () => { - - describe('validate binary protocol checksum', () => { - - it('should produce valid checksums', () => { - // data captured using Wireshark: mapping from AAP packet string to its corresponding ATP CRC32 - const ATP_CRC_LOOKUP = { - '\x34': 0x0032c637, - '\x54': 0xac9700a0, - '\x41': 0xf743b0bb, - '\x7d': 0x167d464f, - '\x81\x51\x01': 0x281fba26, - '\x81\x51\x02': 0x2a764faf, - '\x81\x51\x03': 0x2baee328, - '\x81\x51\x04': 0x2ea5a4bd, - '\x81\x51\x05': 0x2f7d083a, - '\x81\x51\x06': 0x2d14fdb3, - '\x81\x51\x07': 0x2ccc5134, - '\x81\x51\x08': 0x27027299, - '\x81\x51\x09': 0x26dade1e, - '\x81\x51\x0a': 0x24b32b97, - '\x81\x31\x00': 0x48224ccb, - '\x81\x31\x01': 0x49fae04c, - '\x81\x31\x06': 0x4cf1a7d9, - '\x81\x31\x07': 0x4d290b5e, - '\x81\x60\x01': 0xcaf4d6cf - }; - Object.keys(ATP_CRC_LOOKUP).forEach(key => { - const expectedChecksum = ATP_CRC_LOOKUP[key]; - const buffer = new Buffer(key, 'binary'); - const calculatedChecksum = FreeStyleLibreProtocol.calcCrc32(buffer); - expect(calculatedChecksum).equals(expectedChecksum); - } - ); - }); - }); - - describe('validate text protocol checksum', () => { - - it('should accept valid checksums', () => { - const inputData = [ - ['', 0], - ['\x01\x02\x03\x04\x05', 15] - ]; - inputData.forEach(([data, checksum]) => { - const result = FreeStyleLibreProtocol.validateTextChecksum(data, checksum); - expect(result).deep.equals(true); - } - ); - }); - - it('should decline invalid checksums', () => { - const inputData = [ - ['', 10], - ['\x01\x02\x03\x04\x05', 0], - ['', null], - ['', undefined], - ['', ''], - ]; - inputData.forEach(([data, checksum]) => { - const result = FreeStyleLibreProtocol.validateTextChecksum(data, checksum); - expect(result).deep.equals(false); - } - ); - }); - }); - - }); -}); diff --git a/test/lib/testStruct.js b/test/lib/testStruct.js index 603a368dd1..dc01866b54 100644 --- a/test/lib/testStruct.js +++ b/test/lib/testStruct.js @@ -130,7 +130,22 @@ describe('struct.js', function(){ expect(theStruct).itself.to.respondTo('copyBytes'); }); }); - // The legal type characters are and their meaning are described at the top of the struct.js file. +// The legal type characters are: +// b -- a 1-byte unsigned value +// y -- a 1-byte signed value +// s -- a 2-byte unsigned short in little-endian format (0x01 0x00 is returned as 1, not 256) +// S -- a 2-byte unsigned short in big-endian format (0x01 0x00 is returned as 256, not 1) +// i -- a 4-byte unsigned integer in little-endian format +// I -- a 4-byte unsigned integer in big-endian format +// n -- a 4-byte signed integer in little-endian format +// N -- a 4-byte signed integer in big-endian format +// h -- a 2-byte signed integer in little-endian format +// H -- a 2-byte signed integer in big-endian format +// z -- a zero-terminated string of maximum length controlled by the size parameter. +// Z -- a string of bytes with the length controlled by the size parameter. +// f -- a 4-byte float in little-endian format +// F -- a 4-byte float in big-endian format +// . -- the appropriate number of bytes is ignored (used for padding) describe('structlen and format parsing', function(){ it('work properly', function(){ expect(theStruct.structlen('b6.Si')).to.equal(13); @@ -181,15 +196,6 @@ describe('struct.js', function(){ var s = String.fromCharCode.apply(null, buf); expect(s).to.equal('\u0055\u0055\u0055\u0055\u00AA\u00AA\u00AA\u00AA\u00FF\u00FF\u00FF\u00FF\u0000\u0000\u0000\u0000'); }); - it('works for B', function(){ - var buf = new Uint8Array(4); - var inputBuf = [1, 2, 3, 4]; - var len = theStruct.pack(buf, 0, '4B', inputBuf); - expect(len).to.equal(4); - var result = String.fromCharCode.apply(null, buf); - var expected = String.fromCharCode.apply(null, inputBuf); - expect(result).to.equal(expected); - }); it('works for z', function(){ var buf = new Uint8Array(8); var len = theStruct.pack(buf, 0, '8z', 'banana'); diff --git a/yarn.lock b/yarn.lock index 2d2676cc3b..b96cd73d23 100644 --- a/yarn.lock +++ b/yarn.lock @@ -135,10 +135,6 @@ ansi-escapes@^1.1.0, ansi-escapes@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" -ansi-escapes@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-2.0.0.tgz#5bae52be424878dd9783e8910e3fc2922e83c81b" - ansi-html@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" @@ -242,9 +238,9 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" -aria-query@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-0.7.0.tgz#4af10a1e61573ddea0cf3b99b51c52c05b424d24" +aria-query@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-0.3.0.tgz#cb8a9984e2862711c83c80ade5b8f5ca0de2b467" dependencies: ast-types-flow "0.0.7" @@ -278,13 +274,6 @@ array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" -array-includes@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" - dependencies: - define-properties "^1.1.2" - es-abstract "^1.7.0" - array-index@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-index/-/array-index-1.0.0.tgz#ec56a749ee103e4e08c790b9c353df16055b97f9" @@ -306,6 +295,13 @@ array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" +array.prototype.find@^2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.0.4.tgz#556a5c5362c08648323ddaeb9de9d14bc1864c90" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.7.0" + arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -435,19 +431,13 @@ axe-core@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-2.2.0.tgz#00b410b3fc899207d4f2f8e3753cff150d34e4bb" -axobject-query@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-0.1.0.tgz#62f59dbc59c9f9242759ca349960e7a2fe3c36c0" - dependencies: - ast-types-flow "0.0.7" - babar@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/babar/-/babar-0.0.3.tgz#2f394d4a5918f7e1ae9e5408e9a96f3f935ee1e2" dependencies: colors "~0.6.2" -babel-code-frame@^6.11.0, babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: +babel-code-frame@^6.11.0, babel-code-frame@^6.16.0, babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" dependencies: @@ -781,20 +771,19 @@ babel-plugin-minify-type-constructors@^0.0.4: dependencies: babel-helper-is-void-0 "^0.0.1" -babel-plugin-module-resolver@2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-2.7.1.tgz#18be3c42ddf59f7a456c9e0512cd91394f6e4be1" - dependencies: - find-babel-config "^1.0.1" - glob "^7.1.1" - resolve "^1.2.0" - babel-plugin-react-transform@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/babel-plugin-react-transform/-/babel-plugin-react-transform-2.0.2.tgz#515bbfa996893981142d90b1f9b1635de2995109" dependencies: lodash "^4.6.1" +babel-plugin-resolver@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/babel-plugin-resolver/-/babel-plugin-resolver-1.1.0.tgz#e67c1c1d567fb905434cb587cbdb49fa6e6dd099" + dependencies: + babel-resolver "^1.1.0" + lodash "^4.6.0" + babel-plugin-rewire@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/babel-plugin-rewire/-/babel-plugin-rewire-1.1.0.tgz#a6b966d9d8c06c03d95dcda2eec4e2521519549b" @@ -1474,6 +1463,10 @@ babel-register@^6.16.3, babel-register@^6.24.1, babel-register@^6.26.0, babel-re mkdirp "^0.5.1" source-map-support "^0.4.15" +babel-resolver@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/babel-resolver/-/babel-resolver-1.1.0.tgz#ad705d1a67345840839ec36ab33a0238f307fc41" + babel-runtime@6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.18.0.tgz#0f4177ffd98492ef13b9f823e9994a02584c9078" @@ -1597,10 +1590,6 @@ binary@^0.3.0: buffers "~0.1.1" chainsaw "~0.1.0" -bindings@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" - bl@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.1.tgz#cac328f7bee45730d404b692203fcb590e172d5e" @@ -1751,12 +1740,12 @@ browserify-zlib@^0.1.4: dependencies: pako "~0.2.0" -browserslist@2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.1.4.tgz#cc526af4a1312b7d2e05653e56d0c8ab70c0e053" +browserslist@1.7.5: + version "1.7.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.5.tgz#eca4713897b51e444283241facf3985de49a9e2b" dependencies: - caniuse-lite "^1.0.30000670" - electron-to-chromium "^1.3.11" + caniuse-db "^1.0.30000624" + electron-to-chromium "^1.2.3" browserslist@^1.3.6, browserslist@^1.4.0, browserslist@^1.5.2, browserslist@^1.7.6: version "1.7.7" @@ -1858,18 +1847,14 @@ caniuse-api@^1.5.2: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-db@1.0.30000671: - version "1.0.30000671" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000671.tgz#9f071bbc7b96994638ccbaf47829d58a1577a8ed" +caniuse-db@1.0.30000626: + version "1.0.30000626" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000626.tgz#44363dc86857efaf758fea9faef6a15ed93d8f33" -caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: +caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000624, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: version "1.0.30000718" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000718.tgz#86cdd97987302554934c61e106f4e470f16f993c" -caniuse-lite@^1.0.30000670: - version "1.0.30000718" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000718.tgz#0dd24290beb11310b2d80f6b70a823c2a65a6fad" - capture-stack-trace@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" @@ -1930,7 +1915,7 @@ chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0: +chalk@^2.0.1, chalk@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" dependencies: @@ -2044,7 +2029,7 @@ cli-boxes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" -cli-cursor@^1.0.2: +cli-cursor@^1.0.1, cli-cursor@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" dependencies: @@ -2235,7 +2220,7 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@1.6.0, concat-stream@^1.4.6, concat-stream@^1.5.2, concat-stream@^1.6.0: +concat-stream@1.6.0, concat-stream@^1.4.6, concat-stream@^1.5.2: version "1.6.0" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" dependencies: @@ -2620,13 +2605,13 @@ dateformat@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.0.0.tgz#2743e3abb5c3fc2462e527dca445e04e9f4dee17" -debug@2, debug@2.6.8, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.3.2, debug@^2.4.5, debug@^2.5.1, debug@^2.6.1, debug@^2.6.6, debug@^2.6.8: +debug@2, debug@2.6.8, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.4.5, debug@^2.5.1, debug@^2.6.1, debug@^2.6.6, debug@^2.6.8: version "2.6.8" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" dependencies: ms "2.0.0" -debug@2.2.0, debug@~2.2.0: +debug@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" dependencies: @@ -2812,7 +2797,7 @@ difflet@1.0.1: deep-is "0.1.x" traverse "0.6.x" -doctrine@1.5.0: +doctrine@1.5.0, doctrine@^1.2.2: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" dependencies: @@ -3097,7 +3082,7 @@ electron-rebuild@1.5.7: spawn-rx "^2.0.7" yargs "^3.31.0" -electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.11: +electron-to-chromium@^1.2.3, electron-to-chromium@^1.2.7: version "1.3.18" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.18.tgz#3dcc99da3e6b665f6abbc71c28ad51a2cd731a9c" @@ -3246,7 +3231,7 @@ es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: es6-iterator "2" es6-symbol "~3.1" -es6-iterator@2: +es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512" dependencies: @@ -3254,17 +3239,47 @@ es6-iterator@2: es5-ext "^0.10.14" es6-symbol "^3.1" +es6-map@^0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-set "~0.1.5" + es6-symbol "~3.1.1" + event-emitter "~0.3.5" + es6-promise@^4.0.5: version "4.1.1" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.1.1.tgz#8811e90915d9a0dba36274f0b242dbda78f9c92a" -es6-symbol@^3.0.2, es6-symbol@^3.1, es6-symbol@~3.1: +es6-set@~0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-symbol "3.1.1" + event-emitter "~0.3.5" + +es6-symbol@3.1.1, es6-symbol@^3.0.2, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" dependencies: d "1" es5-ext "~0.10.14" +es6-weak-map@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" + dependencies: + d "1" + es5-ext "^0.10.14" + es6-iterator "^2.0.1" + es6-symbol "^3.1.1" + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -3284,17 +3299,26 @@ escodegen@^1.6.1: optionalDependencies: source-map "~0.2.0" -eslint-config-airbnb-base@^11.3.0: +escope@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" + dependencies: + es6-map "^0.1.3" + es6-weak-map "^2.0.1" + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-config-airbnb-base@^11.1.0: version "11.3.2" resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-11.3.2.tgz#8703b11abe3c88ac7ec2b745b7fdf52e00ae680a" dependencies: eslint-restricted-globals "^0.1.1" -eslint-config-airbnb@15.1.0: - version "15.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-15.1.0.tgz#fd432965a906e30139001ba830f58f73aeddae8e" +eslint-config-airbnb@14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-14.1.0.tgz#355d290040bbf8e00bf8b4b19f4b70cbe7c2317f" dependencies: - eslint-config-airbnb-base "^11.3.0" + eslint-config-airbnb-base "^11.1.0" eslint-formatter-pretty@1.1.0: version "1.1.0" @@ -3306,12 +3330,13 @@ eslint-formatter-pretty@1.1.0: plur "^2.1.2" string-width "^2.0.0" -eslint-import-resolver-node@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.1.tgz#4422574cde66a9a7b099938ee4d508a199e0e3cc" +eslint-import-resolver-node@^0.2.0: + version "0.2.3" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz#5add8106e8c928db2cba232bcd9efa846e3da16c" dependencies: - debug "^2.6.8" - resolve "^1.2.0" + debug "^2.2.0" + object-assign "^4.0.1" + resolve "^1.1.6" eslint-import-resolver-webpack@0.8.1: version "0.8.1" @@ -3329,128 +3354,113 @@ eslint-import-resolver-webpack@0.8.1: resolve "^1.2.0" semver "^5.3.0" -eslint-module-utils@^2.1.1: +eslint-module-utils@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz#abaec824177613b8a95b299639e1b6facf473449" dependencies: debug "^2.6.8" pkg-dir "^1.0.0" -eslint-plugin-compat@1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-compat/-/eslint-plugin-compat-1.0.4.tgz#76e52038119a5080e2612cc4141d687f4d140398" +eslint-plugin-compat@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-compat/-/eslint-plugin-compat-1.0.2.tgz#914a8fb93a96950140ff902ad2890930e901046c" dependencies: babel-runtime "^6.23.0" - browserslist "2.1.4" - caniuse-db "1.0.30000671" + browserslist "1.7.5" + caniuse-db "1.0.30000626" requireindex "^1.1.0" -eslint-plugin-import@2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.7.0.tgz#21de33380b9efb55f5ef6d2e210ec0e07e7fa69f" +eslint-plugin-import@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.2.0.tgz#72ba306fad305d67c4816348a4699a4229ac8b4e" dependencies: builtin-modules "^1.1.1" contains-path "^0.1.0" - debug "^2.6.8" + debug "^2.2.0" doctrine "1.5.0" - eslint-import-resolver-node "^0.3.1" - eslint-module-utils "^2.1.1" + eslint-import-resolver-node "^0.2.0" + eslint-module-utils "^2.0.0" has "^1.0.1" lodash.cond "^4.3.0" minimatch "^3.0.3" - read-pkg-up "^2.0.0" + pkg-up "^1.0.0" -eslint-plugin-jsx-a11y@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.1.1.tgz#5c96bb5186ca14e94db1095ff59b3e2bd94069b1" +eslint-plugin-jsx-a11y@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-4.0.0.tgz#779bb0fe7b08da564a422624911de10061e048ee" dependencies: - aria-query "^0.7.0" - array-includes "^3.0.3" + aria-query "^0.3.0" ast-types-flow "0.0.7" - axobject-query "^0.1.0" damerau-levenshtein "^1.0.0" emoji-regex "^6.1.0" - jsx-ast-utils "^1.4.0" - -eslint-plugin-lodash@2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-lodash/-/eslint-plugin-lodash-2.4.3.tgz#8d6f7efebdeaeed0f338cf28405efddca247fc64" - dependencies: - lodash "~4.17.0" + jsx-ast-utils "^1.0.0" + object-assign "^4.0.1" -eslint-plugin-mocha@4.11.0: - version "4.11.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-mocha/-/eslint-plugin-mocha-4.11.0.tgz#91193a2f55e20a5e35974054a0089d30198ee578" +eslint-plugin-mocha@4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-mocha/-/eslint-plugin-mocha-4.9.0.tgz#917a8b499ab8d0c01d69c6e4f81d362ee099b6fd" dependencies: - ramda "^0.24.1" + ramda "^0.23.0" eslint-plugin-promise@3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.5.0.tgz#78fbb6ffe047201627569e85a6c5373af2a68fca" -eslint-plugin-react@7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.3.0.tgz#ca9368da36f733fbdc05718ae4e91f778f38e344" +eslint-plugin-react@6.10.3: + version "6.10.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz#c5435beb06774e12c7db2f6abaddcbf900cd3f78" dependencies: - doctrine "^2.0.0" + array.prototype.find "^2.0.1" + doctrine "^1.2.2" has "^1.0.1" - jsx-ast-utils "^2.0.0" - prop-types "^15.5.10" + jsx-ast-utils "^1.3.4" + object.assign "^4.0.4" eslint-restricted-globals@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7" -eslint-scope@^3.7.1: - version "3.7.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" +eslint@3.19.0: + version "3.19.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc" dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint@4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.5.0.tgz#bb75d3b8bde97fb5e13efcd539744677feb019c3" - dependencies: - ajv "^5.2.0" - babel-code-frame "^6.22.0" - chalk "^2.1.0" - concat-stream "^1.6.0" - cross-spawn "^5.1.0" - debug "^2.6.8" + babel-code-frame "^6.16.0" + chalk "^1.1.3" + concat-stream "^1.5.2" + debug "^2.1.1" doctrine "^2.0.0" - eslint-scope "^3.7.1" - espree "^3.5.0" + escope "^3.6.0" + espree "^3.4.0" esquery "^1.0.0" estraverse "^4.2.0" esutils "^2.0.2" file-entry-cache "^2.0.0" - functional-red-black-tree "^1.0.1" - glob "^7.1.2" - globals "^9.17.0" - ignore "^3.3.3" + glob "^7.0.3" + globals "^9.14.0" + ignore "^3.2.0" imurmurhash "^0.1.4" - inquirer "^3.0.6" + inquirer "^0.12.0" + is-my-json-valid "^2.10.0" is-resolvable "^1.0.0" - js-yaml "^3.9.1" - json-stable-stringify "^1.0.1" + js-yaml "^3.5.1" + json-stable-stringify "^1.0.0" levn "^0.3.0" - lodash "^4.17.4" - minimatch "^3.0.2" - mkdirp "^0.5.1" + lodash "^4.0.0" + mkdirp "^0.5.0" natural-compare "^1.4.0" optionator "^0.8.2" - path-is-inside "^1.0.2" - pluralize "^4.0.0" - progress "^2.0.0" - require-uncached "^1.0.3" - semver "^5.3.0" - strip-ansi "^4.0.0" + path-is-inside "^1.0.1" + pluralize "^1.2.1" + progress "^1.1.8" + require-uncached "^1.0.2" + shelljs "^0.7.5" + strip-bom "^3.0.0" strip-json-comments "~2.0.1" - table "^4.0.1" + table "^3.7.8" text-table "~0.2.0" + user-home "^2.0.0" -espree@^3.5.0: +espree@^3.4.0: version "3.5.0" resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.0.tgz#98358625bdd055861ea27e2867ea729faf463d8d" dependencies: @@ -3494,6 +3504,13 @@ etag@~1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.0.tgz#6f631aef336d6c46362b51764044ce216be3c051" +event-emitter@~0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + dependencies: + d "1" + es5-ext "~0.10.14" + eventemitter3@1.x.x: version "1.2.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" @@ -3623,7 +3640,7 @@ extend@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" -external-editor@^2.0.1, external-editor@^2.0.4: +external-editor@^2.0.1: version "2.0.4" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.0.4.tgz#1ed9199da9cbfe2ef2f7a31b2fde8b0d12368972" dependencies: @@ -3724,6 +3741,13 @@ fd-slicer@~1.0.1: dependencies: pend "~1.2.0" +figures@^1.3.5: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -3776,13 +3800,6 @@ finalhandler@~1.0.0, finalhandler@~1.0.4: statuses "~1.3.1" unpipe "~1.0.0" -find-babel-config@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-1.1.0.tgz#acc01043a6749fec34429be6b64f542ebb5d6355" - dependencies: - json5 "^0.5.1" - path-exists "^3.0.0" - find-cache-dir@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" @@ -4022,7 +4039,7 @@ fsevents@^1.0.0: nan "^2.3.0" node-pre-gyp "^0.6.36" -fstream-ignore@^1.0.0, fstream-ignore@^1.0.5, fstream-ignore@~1.0.5: +fstream-ignore@^1.0.0, fstream-ignore@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" dependencies: @@ -4054,8 +4071,8 @@ fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2, fstream@~1.0.10, fstream@~1.0.8 rimraf "2" function-bind@^1.0.2, function-bind@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + version "1.1.0" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" function.prototype.name@^1.0.0: version "1.0.3" @@ -4065,10 +4082,6 @@ function.prototype.name@^1.0.0: function-bind "^1.1.0" is-callable "^1.1.3" -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - gauge@~1.2.5: version "1.2.7" resolved "https://registry.yarnpkg.com/gauge/-/gauge-1.2.7.tgz#e9cec5483d3d4ee0ef44b60a7d99e4935e136d93" @@ -4230,7 +4243,7 @@ glob@^6.0.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1: +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@~7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -4275,7 +4288,7 @@ global@^4.3.0: min-document "^2.19.0" process "~0.5.1" -globals@^9.17.0, globals@^9.18.0: +globals@^9.14.0, globals@^9.18.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" @@ -4632,7 +4645,7 @@ iferr@^0.1.5, iferr@~0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" -ignore@^3.3.3: +ignore@^3.2.0: version "3.3.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.4.tgz#85ab6d0a9ca8b27b31604c09efe1c14dc21ab872" @@ -4644,10 +4657,6 @@ image-ssim@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/image-ssim/-/image-ssim-0.2.0.tgz#83b42c7a2e6e4b85505477fe6917f5dbc56420e5" -immediate@~3.0.5: - version "3.0.6" - resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" - import-lazy@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" @@ -4702,23 +4711,22 @@ init-package-json@~1.9.3, init-package-json@~1.9.4: validate-npm-package-license "^3.0.1" validate-npm-package-name "^3.0.0" -inquirer@^3.0.6: - version "3.2.2" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.2.2.tgz#c2aaede1507cc54d826818737742d621bef2e823" +inquirer@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" dependencies: - ansi-escapes "^2.0.0" - chalk "^2.0.0" - cli-cursor "^2.1.0" + ansi-escapes "^1.1.0" + ansi-regex "^2.0.0" + chalk "^1.0.0" + cli-cursor "^1.0.1" cli-width "^2.0.0" - external-editor "^2.0.4" - figures "^2.0.0" + figures "^1.3.5" lodash "^4.3.0" - mute-stream "0.0.7" - run-async "^2.2.0" - rx-lite "^4.0.8" - rx-lite-aggregates "^4.0.8" - string-width "^2.1.0" - strip-ansi "^4.0.0" + readline2 "^1.0.1" + run-async "^0.1.0" + rx-lite "^3.1.2" + string-width "^1.0.1" + strip-ansi "^3.0.0" through "^2.3.6" inquirer@~3.0.6: @@ -4858,7 +4866,7 @@ is-glob@^2.0.0, is-glob@^2.0.1: dependencies: is-extglob "^1.0.0" -is-my-json-valid@^2.12.4: +is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4: version "2.16.1" resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz#5a846777e2c2620d1e69104e5d3a03b1f6088f11" dependencies: @@ -5056,7 +5064,7 @@ js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" -js-yaml@^3.8.3, js-yaml@^3.8.4, js-yaml@^3.9.1: +js-yaml@^3.5.1, js-yaml@^3.8.3, js-yaml@^3.8.4: version "3.9.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.9.1.tgz#08775cebdfdd359209f0d2acd383c8f86a6904a0" dependencies: @@ -5130,7 +5138,7 @@ json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" -json-stable-stringify@^1.0.1: +json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" dependencies: @@ -5177,16 +5185,10 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -jsx-ast-utils@^1.4.0: +jsx-ast-utils@^1.0.0, jsx-ast-utils@^1.3.4: version "1.4.1" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1" -jsx-ast-utils@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.0.tgz#ec06a3d60cf307e5e119dac7bad81e89f096f0f8" - dependencies: - array-includes "^3.0.3" - kind-of@^3.0.2: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -5253,12 +5255,6 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -lie@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" - dependencies: - immediate "~3.0.5" - lighthouse@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/lighthouse/-/lighthouse-2.0.0.tgz#55962239ab858eadf46af03468ae1ca17703fde2" @@ -5658,7 +5654,7 @@ lodash@4.5.1: version "4.5.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.5.1.tgz#80e8a074ca5f3893a6b1c10b2a636492d710c316" -lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.16.6, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.6.1, lodash@^4.8.0, lodash@~4.17.0, lodash@~4.17.4: +lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.16.6, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.6.0, lodash@^4.6.1, lodash@^4.8.0, lodash@~4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -5836,18 +5832,14 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -"mime-db@>= 1.29.0 < 2": - version "1.30.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" +"mime-db@>= 1.29.0 < 2", mime-db@~1.29.0: + version "1.29.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.29.0.tgz#48d26d235589651704ac5916ca06001914266878" mime-db@~1.12.0: version "1.12.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.12.0.tgz#3d0c63180f458eb10d325aaa37d7c58ae312e9d7" -mime-db@~1.29.0: - version "1.29.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.29.0.tgz#48d26d235589651704ac5916ca06001914266878" - mime-types@^2.1.11, mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.16, mime-types@~2.1.7: version "2.1.16" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.16.tgz#2b858a52e5ecd516db897ac2be87487830698e23" @@ -6009,6 +6001,10 @@ multipipe@^0.1.2: dependencies: duplexer2 "0.0.2" +mute-stream@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" + mute-stream@0.0.7, mute-stream@~0.0.4: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" @@ -6021,7 +6017,7 @@ mz@^2.3.1: object-assign "^4.0.1" thenify-all "^1.0.0" -nan@^2.2.0, nan@^2.3.0, nan@^2.4.0: +nan@^2.2.0, nan@^2.3.0: version "2.6.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" @@ -6107,13 +6103,6 @@ node-gyp@~3.3.0: tar "^2.0.0" which "1" -node-hid@0.5.4: - version "0.5.4" - resolved "https://registry.yarnpkg.com/node-hid/-/node-hid-0.5.4.tgz#a7246dfc08d52774147fa264354d5da6eab40253" - dependencies: - nan "^2.4.0" - node-pre-gyp "0.6.31" - node-libs-browser@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-0.7.0.tgz#3e272c0819e308935e26674408d7af0e1491b83b" @@ -6170,21 +6159,7 @@ node-libs-browser@^1.0.0: util "^0.10.3" vm-browserify "0.0.4" -node-pre-gyp@0.6.31: - version "0.6.31" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.31.tgz#d8a00ddaa301a940615dbcc8caad4024d58f6017" - dependencies: - mkdirp "~0.5.1" - nopt "~3.0.6" - npmlog "^4.0.0" - rc "~1.1.6" - request "^2.75.0" - rimraf "~2.5.4" - semver "~5.3.0" - tar "~2.2.1" - tar-pack "~3.3.0" - -node-pre-gyp@^0.6.32, node-pre-gyp@^0.6.36, node-pre-gyp@~0.6.32: +node-pre-gyp@^0.6.36, node-pre-gyp@~0.6.32: version "0.6.36" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786" dependencies: @@ -6535,7 +6510,7 @@ npmi@1.0.1: are-we-there-yet "~1.1.2" gauge "~1.2.5" -"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" dependencies: @@ -6619,7 +6594,7 @@ object-keys@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" -object.assign@^4.0.3, object.assign@^4.0.4: +object.assign@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.0.4.tgz#b1c9cc044ef1b9fe63606fc141abbb32e14730cc" dependencies: @@ -6869,7 +6844,7 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -path-is-inside@^1.0.1, path-is-inside@^1.0.2, path-is-inside@~1.0.0, path-is-inside@~1.0.1: +path-is-inside@^1.0.1, path-is-inside@~1.0.0, path-is-inside@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" @@ -6956,6 +6931,12 @@ pkg-dir@^1.0.0: dependencies: find-up "^1.0.0" +pkg-up@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-1.0.0.tgz#3e08fb461525c4421624a33b9f7e6d0af5b05a26" + dependencies: + find-up "^1.0.0" + plist@^2.0.1, plist@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/plist/-/plist-2.1.0.tgz#57ccdb7a0821df21831217a3cad54e3e146a1025" @@ -6970,9 +6951,9 @@ plur@^2.1.2: dependencies: irregular-plurals "^1.0.0" -pluralize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-4.0.0.tgz#59b708c1c0190a2f692f1c7618c446b052fd1762" +pluralize@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" postcss-calc@^5.2.0: version "5.3.1" @@ -7213,11 +7194,11 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0 supports-color "^3.2.3" postcss@^6.0.1: - version "6.0.10" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.10.tgz#c311b89734483d87a91a56dc9e53f15f4e6e84e4" + version "6.0.9" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.9.tgz#54819766784a51c65b1ec4d54c2f93765438c35a" dependencies: chalk "^2.1.0" - source-map "^0.5.7" + source-map "^0.5.6" supports-color "^4.2.1" prelude-ls@~1.1.2: @@ -7269,9 +7250,9 @@ progress-stream@^1.1.0: speedometer "~0.1.2" through2 "~0.2.3" -progress@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" +progress@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" promise@^7.1.1: version "7.3.1" @@ -7291,7 +7272,7 @@ promzard@^0.3.0: dependencies: read "1" -prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8: +prop-types@^15.0.0, prop-types@^15.5.4, prop-types@^15.5.8: version "15.5.10" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" dependencies: @@ -7394,9 +7375,9 @@ querystringify@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb" -ramda@^0.24.1: - version "0.24.1" - resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857" +ramda@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.23.0.tgz#ccd13fff73497a93974e3e86327bfd87bd6e8e2b" randomatic@^1.1.3: version "1.1.7" @@ -7424,15 +7405,6 @@ rc@^1.0.1, rc@^1.1.2, rc@^1.1.6, rc@^1.1.7, rc@^1.2.1: minimist "^1.2.0" strip-json-comments "~2.0.1" -rc@~1.1.6: - version "1.1.7" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.7.tgz#c5ea564bb07aff9fd3a5b32e906c1d3a65940fea" - dependencies: - deep-extend "~0.4.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - react-addons-test-utils@15.5.1: version "15.5.1" resolved "https://registry.yarnpkg.com/react-addons-test-utils/-/react-addons-test-utils-15.5.1.tgz#e0d258cda2a122ad0dff69f838260d0c3958f5f7" @@ -7668,7 +7640,7 @@ readable-stream@~2.0.5: string_decoder "~0.10.x" util-deprecate "~1.0.1" -readable-stream@~2.1.4, readable-stream@~2.1.5: +readable-stream@~2.1.5: version "2.1.5" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" dependencies: @@ -7698,6 +7670,14 @@ readdirp@^2.0.0: readable-stream "^2.0.2" set-immediate-shim "^1.0.1" +readline2@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + mute-stream "0.0.5" + realize-package-specifier@~3.0.1: version "3.0.3" resolved "https://registry.yarnpkg.com/realize-package-specifier/-/realize-package-specifier-3.0.3.tgz#d0def882952b8de3f67eba5e91199661271f41f4" @@ -7884,7 +7864,7 @@ replace-ext@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" -request@2, request@^2.45.0, request@^2.47.0, request@^2.65.0, request@^2.72.0, request@^2.74.0, request@^2.75.0, request@^2.79.0, request@^2.81.0, request@~2.81.0: +request@2, request@^2.45.0, request@^2.47.0, request@^2.65.0, request@^2.72.0, request@^2.74.0, request@^2.79.0, request@^2.81.0, request@~2.81.0: version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" dependencies: @@ -7971,7 +7951,7 @@ require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" -require-uncached@^1.0.3: +require-uncached@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" dependencies: @@ -8057,7 +8037,7 @@ rimraf@2.2.8: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" -rimraf@~2.5.1, rimraf@~2.5.2, rimraf@~2.5.4: +rimraf@~2.5.2, rimraf@~2.5.4: version "2.5.4" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" dependencies: @@ -8074,21 +8054,21 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^2.0.0" inherits "^2.0.1" +run-async@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" + dependencies: + once "^1.3.0" + run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" dependencies: is-promise "^2.1.0" -rx-lite-aggregates@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" - dependencies: - rx-lite "*" - -rx-lite@*, rx-lite@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" +rx-lite@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" rx@2.3.24: version "2.3.24" @@ -8200,18 +8180,6 @@ send@0.15.4: range-parser "~1.2.0" statuses "~1.3.1" -serialport@4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/serialport/-/serialport-4.0.7.tgz#421c618a8a612bd40cfa461b4a46154daf2229a5" - dependencies: - bindings "1.2.1" - commander "^2.9.0" - debug "^2.3.2" - lie "^3.1.0" - nan "^2.4.0" - node-pre-gyp "^0.6.32" - object.assign "^4.0.3" - serve-index@^1.7.2: version "1.9.0" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.0.tgz#d2b280fc560d616ee81b48bf0fa82abed2485ce7" @@ -8297,7 +8265,7 @@ shelljs@0.7.0: interpret "^1.0.0" rechoir "^0.6.2" -shelljs@0.7.8: +shelljs@0.7.8, shelljs@^0.7.5: version "0.7.8" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" dependencies: @@ -8431,7 +8399,7 @@ source-map@0.5.6: version "0.5.6" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" -source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1, source-map@~0.5.3: +source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -8586,7 +8554,7 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string-width@^2.0.0, string-width@^2.1.0: +string-width@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" dependencies: @@ -8740,9 +8708,9 @@ symbol@^0.2.1: version "0.2.3" resolved "https://registry.yarnpkg.com/symbol/-/symbol-0.2.3.tgz#3b9873b8a901e47c6efe21526a3ac372ef28bbc7" -table@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/table/-/table-4.0.1.tgz#a8116c133fac2c61f4a420ab6cdf5c4d61f0e435" +table@^3.7.8: + version "3.8.3" + resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" dependencies: ajv "^4.7.0" ajv-keywords "^1.0.0" @@ -8772,19 +8740,6 @@ tar-pack@^3.4.0: tar "^2.2.1" uid-number "^0.0.6" -tar-pack@~3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.3.0.tgz#30931816418f55afc4d21775afdd6720cee45dae" - dependencies: - debug "~2.2.0" - fstream "~1.0.10" - fstream-ignore "~1.0.5" - once "~1.3.3" - readable-stream "~2.1.4" - rimraf "~2.5.1" - tar "~2.2.1" - uid-number "~0.0.6" - tar-stream@^1.5.0: version "1.5.4" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.4.tgz#36549cf04ed1aee9b2a30c0143252238daf94016" @@ -9051,7 +9006,7 @@ uglify-to-browserify@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" -uid-number@0.0.6, uid-number@^0.0.6, uid-number@~0.0.6: +uid-number@0.0.6, uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" @@ -9166,7 +9121,7 @@ url@^0.11.0, url@~0.11.0: punycode "1.3.2" querystring "0.2.0" -user-home@2.0.0: +user-home@2.0.0, user-home@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" dependencies: