diff --git a/.gitignore b/.gitignore index b8b6cad5141..4fcbc57e59d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ bower_components/ node_modules/ + +bundle/bundle.out.js + .idea/ *.iml my.env @@ -15,3 +18,5 @@ static/bower_components/ # istanbul output coverage/ + +npm-debug.log diff --git a/.travis.yml b/.travis.yml index 99660614597..562c8d9edbb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,8 @@ language: node_js +sudo: false node_js: - - "0.10" - - "0.11" -matrix: - allow_failures: - - node_js: "0.11" -services: - - mongodb -before_script: - - sleep 10 - - echo mongo mongo_travis -script: - - make travis + - "0.10" + - "0.12" +services: mongodb +script: make travis +after_script: make report diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dbac299d8e1..6169a7cbc4a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,19 @@ + + +**Table of Contents** + +- [Contributing to cgm-remote-monitor](#contributing-to-cgm-remote-monitor) + - [Design](#design) + - [Develop on `dev`](#develop-on-dev) + - [Style Guide](#style-guide) + - [Create a prototype](#create-a-prototype) + - [Submit a pull request](#submit-a-pull-request) + - [Comments and issues](#comments-and-issues) + - [Co-ordination](#co-ordination) + - [Other Dev Tips](#other-dev-tips) + + + # Contributing to cgm-remote-monitor @@ -12,15 +28,14 @@ [build-url]: https://travis-ci.org/nightscout/cgm-remote-monitor [dependency-img]: https://img.shields.io/david/nightscout/cgm-remote-monitor.svg [dependency-url]: https://david-dm.org/nightscout/cgm-remote-monitor -[coverage-img]: https://img.shields.io/coveralls/nightscout/cgm-remote-monitor/coverage.svg -[coverage-url]: https://coveralls.io/r/nightscout/cgm-remote-monitor?branch=dev +[coverage-img]: https://img.shields.io/coveralls/nightscout/cgm-remote-monitor/master.svg +[coverage-url]: https://coveralls.io/r/nightscout/cgm-remote-monitor?branch=master [gitter-img]: https://img.shields.io/badge/Gitter-Join%20Chat%20%E2%86%92-1dce73.svg [gitter-url]: https://gitter.im/nightscout/public [ready-img]: https://badge.waffle.io/nightscout/cgm-remote-monitor.svg?label=ready&title=Ready [waffle]: https://waffle.io/nightscout/cgm-remote-monitor [progress-img]: https://badge.waffle.io/nightscout/cgm-remote-monitor.svg?label=in+progress&title=In+Progress - ## Design Participate in the design process by creating an issue to discuss your @@ -31,6 +46,24 @@ design. We develop on the `dev` branch. You can get the dev branch checked out using `git checkout dev`. +## Style Guide + +Some simple rules, that will make it easier to maintain our codebase: + +* All indenting should use 2 space where possible (js, css, html, etc) +* A space before function parameters, such as: `function boom (name, callback) { }`, this makes searching for calls easier +* Name your callback functions, such as `boom('the name', function afterBoom ( result ) { }` +* Don't include author names in the header of your files, if you need to give credit to someone else do it in the commit comment. +* Use the comma first style, for example: + + ```javascript + var data = { + value: 'the value' + , detail: 'the details...' + , time: Date.now() + }; + ``` + ## Create a prototype Fork cgm-remote-monitor and create a branch. @@ -78,3 +111,13 @@ the version correctly. See sem-ver for versioning strategy. Every commit is tested by travis. We encourage adding tests to validate your design. We encourage discussing your use cases to help everyone get a better understanding of your design. + +## Other Dev Tips + +* Join the [Gitter chat][gitter-url] +* Get a local dev environment setup if you haven't already +* Try breaking up big features/improvements into small parts. It's much easier to accept small PR's +* Create tests for your new code, and for the old code too. We are aiming for a full test coverage. +* If your going to be working in old code that needs lots of reformatting consider doing the clean as a separate PR. +* If you can find others to help test your PR is will help get them merged in sooner. + diff --git a/Makefile b/Makefile index 15dc02705ff..2b60d26001b 100644 --- a/Makefile +++ b/Makefile @@ -1,25 +1,51 @@ +# Nightscout tests/builds/analysis TESTS=tests/*.js MONGO_CONNECTION?=mongodb://localhost/test_db CUSTOMCONNSTR_mongo_settings_collection?=test_settings CUSTOMCONNSTR_mongo_collection?=test_sgvs MONGO_SETTINGS=MONGO_CONNECTION=${MONGO_CONNECTION} \ - CUSTOMCONNSTR_mongo_collection=${CUSTOMCONNSTR_mongo_collection} \ - CUSTOMCONNSTR_mongo_settings_collection=${CUSTOMCONNSTR_mongo_settings_collection} + CUSTOMCONNSTR_mongo_collection=${CUSTOMCONNSTR_mongo_collection} + +# XXX.bewest: Mocha is an odd process, and since things are being +# wrapped and transformed, this odd path needs to be used, not the +# normal wrapper. When ./node_modules/.bin/mocha is used, no coverage +# information is generated. This happens because typical shell +# wrapper performs process management that mucks with the test +# coverage reporter's ability to instrument the tests correctly. +# Hard coding it to the local with our pinned version is bigger for +# initial installs, but ensures a consistent environment everywhere. +# On Travis, ./node_modules/.bin and other `nvm` and `npm` bundles are +# inserted into the default `$PATH` enviroinment, making pointing to +# the unwrapped mocha executable necessary. +MOCHA=./node_modules/mocha/bin/_mocha +# Pinned from dependency list. +ISTANBUL=./node_modules/.bin/istanbul +ANALYZED=./coverage/lcov.info +export CODACY_REPO_TOKEN=e29ae5cf671f4f918912d9864316207c all: test -travis-cov: - NODE_ENV=test \ - ${MONGO_SETTINGS} \ - istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -vvv -R tap ${TESTS} && \ - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && \ - rm -rf ./coverage +coverage: + NODE_ENV=test ${MONGO_SETTINGS} \ + ${ISTANBUL} cover ${MOCHA} -- -R tap ${TESTS} + +report: + test -f ${ANALYZED} && \ + (npm install coveralls && cat ${ANALYZED} | \ + ./node_modules/.bin/coveralls) || echo "NO COVERAGE" + test -f ${ANALYZED} && \ + (npm install codecov.io && cat ${ANALYZED} | \ + ./node_modules/codecov.io/bin/codecov.io.js) || echo "NO COVERAGE" + test -f ${ANALYZED} && \ + (npm install codacy-coverage && cat ${ANALYZED} | \ + YOURPACKAGE_COVERAGE=1 ./node_modules/codacy-coverage/bin/codacy-coverage.js) || echo "NO COVERAGE" test: - ${MONGO_SETTINGS} \ - mocha --verbose -vvv -R tap ${TESTS} + ${MONGO_SETTINGS} ${MOCHA} -R tap ${TESTS} -travis: test travis-cov +travis: + NODE_ENV=test ${MONGO_SETTINGS} \ + ${ISTANBUL} cover ${MOCHA} --report lcovonly -- -R tap ${TESTS} -.PHONY: test +.PHONY: all coverage report test travis diff --git a/Procfile b/Procfile index 489b2700aca..32dd1c83adc 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: node server.js +web: ./node_modules/.bin/forever --minUptime 100 -c node server.js diff --git a/README.md b/README.md index e402869fd33..fff1d1180a6 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,12 @@ -cgm-remote-monitor (a.k.a. Nightscout) +Nightscout Web Monitor (a.k.a. cgm-remote-monitor) ====================================== +![nightscout horizontal](https://cloud.githubusercontent.com/assets/751143/8425633/93c94dc0-1ebc-11e5-99e7-71a8f464caac.png) + [![Build Status][build-img]][build-url] [![Dependency Status][dependency-img]][dependency-url] [![Coverage Status][coverage-img]][coverage-url] +[![Codacy Badge][codacy-img]][codacy-url] [![Gitter chat][gitter-img]][gitter-url] [![Stories in Ready][ready-img]][waffle] [![Stories in Progress][progress-img]][waffle] @@ -19,6 +22,8 @@ and blood glucose values are predicted 0.5 hours ahead using an autoregressive second order model. Alarms are generated for high and low values, which can be cleared by any watcher of the data. +# [#WeAreNotWaiting](https://twitter.com/hashtag/wearenotwaiting?src=hash&vertical=default&f=images) and [this](https://vimeo.com/109767890) is why. + Community maintained fork of the [original cgm-remote-monitor][original]. @@ -28,6 +33,8 @@ Community maintained fork of the [dependency-url]: https://david-dm.org/nightscout/cgm-remote-monitor [coverage-img]: https://img.shields.io/coveralls/nightscout/cgm-remote-monitor/master.svg [coverage-url]: https://coveralls.io/r/nightscout/cgm-remote-monitor?branch=master +[codacy-img]: https://www.codacy.com/project/badge/f79327216860472dad9afda07de39d3b +[codacy-url]: https://www.codacy.com/app/Nightscout/cgm-remote-monitor [gitter-img]: https://img.shields.io/badge/Gitter-Join%20Chat%20%E2%86%92-1dce73.svg [gitter-url]: https://gitter.im/nightscout/public [ready-img]: https://badge.waffle.io/nightscout/cgm-remote-monitor.svg?label=ready&title=Ready @@ -37,8 +44,38 @@ Community maintained fork of the [heroku-url]: https://heroku.com/deploy [original]: https://github.com/rnpenguin/cgm-remote-monitor -Install ---------------- + + +**Table of Contents** + +- [Install](#install) +- [Usage](#usage) + - [Updating my version?](#updating-my-version) + - [What is my mongo string?](#what-is-my-mongo-string) + - [Configure my uploader to match](#configure-my-uploader-to-match) + - [Nightscout API](#nightscout-api) + - [Example Queries](#example-queries) + - [Environment](#environment) + - [Required](#required) + - [Features/Labs](#featureslabs) + - [Alarms](#alarms) + - [Core](#core) + - [Predefined values for your browser settings (optional)](#predefined-values-for-your-browser-settings-optional) + - [Plugins](#plugins) + - [Default Plugins](#default-plugins) + - [Built-in/Example Plugins:](#built-inexample-plugins) + - [Extended Settings](#extended-settings) + - [Pushover](#pushover) + - [IFTTT Maker](#ifttt-maker) + - [Treatment Profile](#treatment-profile) + - [Setting environment variables](#setting-environment-variables) + - [Vagrant install](#vagrant-install) + - [More questions?](#more-questions) + - [License](#license) + + + +# Install Requirements: @@ -50,62 +87,95 @@ Clone this repo then install dependencies into the root of the project: $ npm install ``` -Usage ---------------- +#Usage The data being uploaded from the server to the client is from a -MongoDB server such as [mongolab][mongodb]. In order to access the -database, the appropriate credentials need to be filled into the -[JSON][json] file in the root directory. SGV data from the database -is assumed to have the following fields: date, sgv. Once all that is -ready, just host your web app on your service of choice. +MongoDB server such as [mongolab][mongodb]. [mongodb]: https://mongolab.com -[json]: https://github.com/rnpenguin/cgm-remote-monitor/blob/master/database_configuration.json [autoconfigure]: http://nightscout.github.io/pages/configure/ [mongostring]: http://nightscout.github.io/pages/mongostring/ [update-fork]: http://nightscout.github.io/pages/update-fork/ -### Updating my version? +## Updating my version? The easiest way to update your version of cgm-remote-monitor to our latest recommended version is to use the [update my fork tool][update-fork]. It even gives out stars if you are up to date. -### What is my mongo string? +## What is my mongo string? Try the [what is my mongo string tool][mongostring] to get a good idea of your mongo string. You can copy and paste the text in the gray box into your `MONGO_CONNECTION` environment variable. -### Configure my uploader to match +## Configure my uploader to match Use the [autoconfigure tool][autoconfigure] to sync an uploader to your config. -### Environment +## Nightscout API + +The Nightscout API enables direct access to your DData without the need for direct Mongo access. +You can find CGM data in `/api/v1/entries`, Care Portal Treatments in `/api/v1/treatments`, and Treatment Profiles in `/api/v1/profile`. +The server status and settings are available from `/api/v1/status.json`. + +By default the `/entries` and `/treatments` APIs limit results to the the most recent 10 values from the last 2 days. +You can get many more results, by using the `count`, `date`, `dateString`, and `created_at` parameters, depending on the type of data you're looking for. + +#### Example Queries + +(replace `http://localhost:1337` with your base url, YOUR-SITE) + + * 100's: `http://localhost:1337/api/v1/entries.json?find[sgv]=100` + * BGs between 2 days: `http://localhost:1337/api/v1/entries/sgv.json?find[dateString][$gte]=2015-08-28&find[dateString][$lte]=2015-08-30` + * Juice Box corrections in a year: `http://localhost:1337/api/v1/treatments.json?count=1000&find[carbs]=15&find[eventType]=Carb+Correction&find[created_at][$gte]=2015` + * Boluses over 2U: `http://localhost:1337/api/v1/treatments.json?find[insulin][$gte]=2` + +The API is Swagger enabled, so you can generate client code to make working with the API easy. +To learn more about the Nightscout API, visit https://YOUR-SITE.com/api-docs.html or review [swagger.yaml](swagger.yaml). + + +## Environment `VARIABLE` (default) - description -#### Required +### Required * `MONGO_CONNECTION` - Your mongo uri, for example: `mongodb://sally:sallypass@ds099999.mongolab.com:99999/nightscout` + * `DISPLAY_UNITS` (`mg/dl`) - Choices: `mg/dl` and `mmol`. Setting to `mmol` puts the entire server into `mmol` mode by default, no further settings needed. + * `BASE_URL` - Used for building links to your sites api, ie pushover callbacks, usually the URL of your Nightscout site you may want https instead of http -#### Features/Labs +### Features/Labs - * `ENABLE` - Used to enable optional features, currently supports: `careportal` + * `ENABLE` - Used to enable optional features, expects a space delimited list, such as: `careportal rawbg iob`, see [plugins](#plugins) below + * `DISABLE` - Used to disable default features, expects a space delimited list, such as: `direction upbat`, see [plugins](#plugins) below * `API_SECRET` - A secret passphrase that must be at least 12 characters long, required to enable `POST` and `PUT`; also required for the Care Portal + * `TREATMENTS_AUTH` (`off`) - possible values `on` or `off`. When on device must be authenticated by entering `API_SECRET` to create treatments + + +### Alarms + + These alarm setting effect all delivery methods (browser, pushover, maker, etc), some settings can be overridden per client (web browser) + + * `ALARM_TYPES` (`simple` if any `BG_`* ENV's are set, otherwise `predict`) - currently 2 alarm types are supported, and can be used independently or combined. The `simple` alarm type only compares the current BG to `BG_` thresholds above, the `predict` alarm type uses highly tuned formula that forecasts where the BG is going based on it's trend. `predict` **DOES NOT** currently use any of the `BG_`* ENV's * `BG_HIGH` (`260`) - must be set using mg/dl units; the high BG outside the target range that is considered urgent * `BG_TARGET_TOP` (`180`) - must be set using mg/dl units; the top of the target range, also used to draw the line on the chart * `BG_TARGET_BOTTOM` (`80`) - must be set using mg/dl units; the bottom of the target range, also used to draw the line on the chart * `BG_LOW` (`55`) - must be set using mg/dl units; the low BG outside the target range that is considered urgent - * `ALARM_TYPES` (`simple` if any `BG_`* ENV's are set, otherwise `predict`) - currently 2 alarm types are supported, and can be used independently or combined. The `simple` alarm type only compares the current BG to `BG_` thresholds above, the `predict` alarm type uses highly tuned formula that forecasts where the BG is going based on it's trend. `predict` **DOES NOT** currently use any of the `BG_`* ENV's - * `PUSHOVER_API_TOKEN` - Used to enable pushover notifications for Care Portal treatments, this token is specific to the application you create from in [Pushover](https://pushover.net/) - * `PUSHOVER_USER_KEY` - Your Pushover user key, can be found in the top left of the [Pushover](https://pushover.net/) site + * `ALARM_URGENT_HIGH` (`on`) - possible values `on` or `off` + * `ALARM_URGENT_HIGH_MINS` (`30 60 90 120`) - Number of minutes to snooze urgent high alarms, space separated for options in browser, first used for pushover + * `ALARM_HIGH` (`on`) - possible values `on` or `off` + * `ALARM_HIGH_MINS` (`30 60 90 120`) - Number of minutes to snooze high alarms, space separated for options in browser, first used for pushover + * `ALARM_LOW` (`on`) - possible values `on` or `off` + * `ALARM_LOW_MINS` (`15 30 45 60`) - Number of minutes to snooze low alarms, space separated for options in browser, first used for pushover + * `ALARM_URGENT_LOW` (`on`) - possible values `on` or `off` + * `ALARM_URGENT_LOW_MINS` (`15 30 45`) - Number of minutes to snooze urgent low alarms, space separated for options in browser, first used for pushover + * `ALARM_URGENT_MINS` (`30 60 90 120`) - Number of minutes to snooze urgent alarms (that aren't tagged as high or low), space separated for options in browser, first used for pushover + * `ALARM_WARN_MINS` (`30 60 90 120`) - Number of minutes to snooze warning alarms (that aren't tagged as high or low), space separated for options in browser, first used for pushover -#### Core +### Core - * `DISPLAY_UNITS` (`mg/dl`) - Choices: `mg/dl` and `mmol`. Setting to `mmol` puts the entire server into `mmol` mode by default, no further settings needed. * `MONGO_COLLECTION` (`entries`) - The collection used to store SGV, MBG, and CAL records from your CGM device * `MONGO_TREATMENTS_COLLECTION` (`treatments`) -The collection used to store treatments entered in the Care Portal, see the `ENABLE` env var above * `MONGO_DEVICESTATUS_COLLECTION`(`devicestatus`) - The collection used to store device status information such as uploader battery @@ -113,12 +183,161 @@ Use the [autoconfigure tool][autoconfigure] to sync an uploader to your config. * `SSL_KEY` - Path to your ssl key file, so that ssl(https) can be enabled directly in node.js * `SSL_CERT` - Path to your ssl cert file, so that ssl(https) can be enabled directly in node.js * `SSL_CA` - Path to your ssl ca file, so that ssl(https) can be enabled directly in node.js + * `HEARTBEAT` (`60`) - Number of seconds to wait in between database checks + + +### Predefined values for your browser settings (optional) + * `TIME_FORMAT` (`12`)- possible values `12` or `24` + * `NIGHT_MODE` (`off`) - possible values `on` or `off` + * `SHOW_RAWBG` (`never`) - possible values `always`, `never` or `noise` + * `CUSTOM_TITLE` (`Nightscout`) - Usually name of T1 + * `THEME` (`default`) - possible values `default` or `colors` + * `ALARM_TIMEAGO_WARN` (`on`) - possible values `on` or `off` + * `ALARM_TIMEAGO_WARN_MINS` (`15`) - minutes since the last reading to trigger a warning + * `ALARM_TIMEAGO_URGENT` (`on`) - possible values `on` or `off` + * `ALARM_TIMEAGO_URGENT_MINS` (`30`) - minutes since the last reading to trigger a urgent alarm + * `SHOW_PLUGINS` - enabled plugins that should have their visualizations shown, defaults to all enabled + * `LANGUAGE` (`en`) - language of Nighscout. If not available english is used + +### Plugins + + Plugins are used extend the way information is displayed, how notifications are sent, alarms are triggered, and more. + + The built-in/example plugins that are available by default are listed below. The plugins may still need to be enabled by adding to the `ENABLE` environment variable. + +#### Default Plugins + + These can be disabled by setting the `DISABLE` env var, for example `DISABLE="direction upbat"` + + * `delta` (BG Delta) - Calculates and displays the change between the last 2 BG values. + * `direction` (BG Direction) - Displays the trend direction. + * `upbat` (Uploader Battery) - Displays the most recent battery status from the uploader phone. + * `errorcodes` (CGM Error Codes) - Generates alarms for CGM codes `9` (hourglass) and `10` (???). + * Use [extended settings](#extended-settings) to adjust what errorcodes trigger notifications and alarms: + * `ERRORCODES_INFO` (`1 2 3 4 5 6 7 8`) - By default the needs calibration (blood drop) and other codes below 9 generate an info level notification, set to a space separate list of number or `off` to disable + * `ERRORCODES_WARN` (`off`) - By default there are no warning configured, set to a space separate list of numbers or `off` to disable + * `ERRORCODES_URGENT` (`9 10`) - By default the hourglass and ??? generate an urgent alarm, set to a space separate list of numbers or `off` to disable + * `ar2` ([Forcasting using AR2 algorithm](https://github.com/nightscout/nightscout.github.io/wiki/Forecasting)) - Generates alarms based on forecasted values. + * Enabled by default if no thresholds are set **OR** `ALARM_TYPES` includes `predict`. + * Use [extended settings](#extended-settings) to adjust AR2 behavior: + * `AR2_USE_RAW` (`false`) - to forecast using `rawbg` values when standard values don't trigger an alarm. + * `AR2_CONE_FACTOR` (`2`) - to adjust size of cone, use `0` for a single line. + * `simplealarms` (Simple BG Alarms) - Uses `BG_HIGH`, `BG_TARGET_TOP`, `BG_TARGET_BOTTOM`, `BG_LOW` thresholds to generate alarms. + * Enabled by default if 1 of these thresholds is set **OR** `ALARM_TYPES` includes `simple`. + +#### Built-in/Example Plugins: + + * `rawbg` (Raw BG) - Calculates BG using sensor and calibration records from and displays an alternate BG values and noise levels. + * `iob` (Insulin-on-Board) - Adds the IOB pill visualization in the client and calculates values that used by other plugins. Uses treatments with insulin doses and the `dia` and `sens` fields from the [treatment profile](#treatment-profile). + * `cob` (Carbs-on-Board) - Adds the COB pill visualization in the client and calculates values that used by other plugins. Uses treatments with carb doses and the `carbs_hr`, `carbratio`, and `sens` fields from the [treatment profile](#treatment-profile). + * `bwp` (Bolus Wizard Preview) - This plugin in intended for the purpose of automatically snoozing alarms when the CGM indicates high blood sugar but there is also insulin on board (IOB) and secondly, alerting to user that it might be beneficial to measure the blood sugar using a glucometer and dosing insulin as calculated by the pump or instructed by trained medicare professionals. ***The values provided by the plugin are provided as a reference based on CGM data and insulin sensitivity you have configured, and are not intended to be used as a reference for bolus calculation.*** The plugin calculates the bolus amount when above your target, generates alarms when you should consider checking and bolusing, and snoozes alarms when there is enough IOB to cover a high BG. Uses the results of the `iob` plugin and `sens`, `target_high`, and `target_low` fields from the [treatment profile](#treatment-profile). Defaults that can be adjusted with [extended setting](#extended-settings) + * `BWP_WARN` (`0.50`) - If `BWP` is > `BWP_WARN` a warning alarm will be triggered. + * `BWP_URGENT` (`1.00`) - If `BWP` is > `BWP_URGENT` an urgent alarm will be triggered. + * `BWP_SNOOZE_MINS` (`10`) - minutes to snooze when there is enough IOB to cover a high BG. + * `BWP_SNOOZE` - (`0.10`) If BG is higher then the `target_high` and `BWP` < `BWP_SNOOZE` alarms will be snoozed for `BWP_SNOOZE_MINS`. + * `cage` (Cannula Age) - Calculates the number of hours since the last `Site Change` treatment that was recorded. + * `CAGE_ENABLE_ALERTS` (`false`) - Set to `true` to enable notifications to remind you of upcoming cannula change. + * `CAGE_INFO` (`44`) - If time since last `Site Change` matches `CAGE_INFO`, user will be warned of upcoming cannula change + * `CAGE_WARN` (`48`) - If time since last `Site Change` matches `CAGE_WARN`, user will be alarmed to to change the cannula + * `CAGE_URGENT` (`72`) - If time since last `Site Change` matches `CAGE_URGENT`, user will be issued a persistent warning of overdue change. + * `treatmentnotify` (Treatment Notifications) - Generates notifications when a treatment has been entered and snoozes alarms minutes after a treatment. Default snooze is 10 minutes, and can be set using the `TREATMENTNOTIFY_SNOOZE_MINS` [extended setting](#extended-settings). + * `basal` (Basal Profile) - Adds the Basal pill visualization to display the basal rate for the current time. Also enables the `bwp` plugin to calculate correction temp basal suggestions. Uses the `basal` field from the [treatment profile](#treatment-profile). + * `bridge` (Share2Nightscout bridge) - Glucose reading directly from the Share service, uses these extended settings: + * `BRIDGE_USER_NAME` - Your user name for the Share service. + * `BRIDGE_PASSWORD` - Your password for the Share service. + * `BRIDGE_INTERVAL` (`150000` *2.5 minutes*) - The time to wait between each update. + * `BRIDGE_MAX_COUNT` (`1`) - The maximum number of records to fetch per update. + * `BRIDGE_FIRST_FETCH_COUNT` (`3`) - Changes max count during the very first update only. + * `BRIDGE_MAX_FAILURES` (`3`) - How many failures before giving up. + * `BRIDGE_MINUTES` (`1400`) - The time window to search for new data per update (default is one day in minutes). + * `mmconnect` (MiniMed Connect bridge) - Transfer real-time MiniMed Connect data from the Medtronic CareLink server into Nightscout ([read more](https://github.com/mddub/minimed-connect-to-nightscout)) + * `MMCONNECT_USER_NAME` - Your user name for CareLink Connect. + * `MMCONNECT_PASSWORD` - Your password for CareLink Connect. + * `MMCONNECT_INTERVAL` (`60000` *1 minute*) - Number of milliseconds to wait between requests to the CareLink server. + * `MMCONNECT_MAX_RETRY_DURATION` (`32`) - Maximum number of total seconds to spend retrying failed requests before giving up. + * `MMCONNECT_SGV_LIMIT` (`24`) - Maximum number of recent sensor glucose values to send to Nightscout on each request. + * `MMCONNECT_VERBOSE` - Set this to any truthy value to log CareLink request information to the console. + + Also see [Pushover](#pushover) and [IFTTT Maker](#ifttt-maker). + + +#### Extended Settings + Some plugins support additional configuration using extra environment variables. These are prefixed with the name of the plugin and a `_`. For example setting `MYPLUGIN_EXAMPLE_VALUE=1234` would make `extendedSettings.exampleValue` available to the `MYPLUGIN` plugin. + + Plugins only have access to their own extended settings, all the extended settings of client plugins will be sent to the browser. + +#### Pushover + In addition to the normal web based alarms, there is also support for [Pushover](https://pushover.net/) based alarms and notifications. + + To get started install the Pushover application on your iOS or Android device and create an account. + + Using that account login to [Pushover](https://pushover.net/), in the top left you’ll see your User Key, you’ll need this plus an application API Token/Key to complete this setup. + + You’ll need to [Create a Pushover Application](https://pushover.net/apps/build). You only need to set the Application name, you can ignore all the other settings, but setting an Icon is a nice touch. Maybe you'd like to use [this one](https://raw.githubusercontent.com/nightscout/cgm-remote-monitor/master/static/images/large.png)? + + Pushover is configured using the following Environment Variables: + + * `ENABLE` - `pushover` should be added to the list of plugin, for example: `ENABLE="pushover"`. + * `PUSHOVER_API_TOKEN` - Used to enable pushover notifications, this token is specific to the application you create from in [Pushover](https://pushover.net/), ***[additional pushover information](#pushover)*** below. + * `PUSHOVER_USER_KEY` - Your Pushover user key, can be found in the top left of the [Pushover](https://pushover.net/) site, this can also be a pushover delivery group key to send to a group rather than just a single user. This also supports a space delimited list of keys. To disable `INFO` level pushes set this to `off`. + * `PUSHOVER_ALARM_KEY` - An optional Pushover user/group key, will be used for system wide alarms (level > `WARN`). If not defined this will fallback to `PUSHOVER_USER_KEY`. A possible use for this is sending important messages and alarms to a CWD that you don't want to send all notification too. This also support a space delimited list of keys. To disable Alarm pushes set this to `off`. + * `PUSHOVER_ANNOUNCEMENT_KEY` - An optional Pushover user/group key, will be used for system wide user generated announcements. If not defined this will fallback to `PUSHOVER_USER_KEY` or `PUSHOVER_ALARM_KEY`. This also support a space delimited list of keys. To disable Announcement pushes set this to `off`. + * `BASE_URL` - Used for pushover callbacks, usually the URL of your Nightscout site, use https when possible. + * `API_SECRET` - Used for signing the pushover callback request for acknowledgments. + + If you never want to get info level notifications (treatments) use `PUSHOVER_USER_KEY="off"` + If you never want to get an alarm via pushover use `PUSHOVER_ALARM_KEY="off"` + If you never want to get an announcement via pushover use `PUSHOVER_ANNOUNCEMENT_KEY="off"` + + If only `PUSHOVER_USER_KEY` is set it will be used for all info notifications, alarms, and announcements + + For testing/development try [localtunnel](http://localtunnel.me/). + +#### IFTTT Maker + In addition to the normal web based alarms, and pushover, there is also integration for [IFTTT Maker](https://ifttt.com/maker). + + With Maker you are able to integrate with all the other [IFTTT Channels](https://ifttt.com/channels). For example you can send a tweet when there is an alarm, change the color of hue light, send an email, send and sms, and so much more. + + 1. Setup IFTTT account: [login](https://ifttt.com/login) or [create an account](https://ifttt.com/join) + 2. Find your secret key on the [maker page](https://ifttt.com/maker) + 3. Configure Nightscout by setting these environment variables: + * `ENABLE` - `maker` should be added to the list of plugin, for example: `ENABLE="maker"`. + * `MAKER_KEY` - Set this to your secret key that you located in step 2, for example: `MAKER_KEY="abcMyExampleabc123defjt1DeNSiftttmak-XQb69p"` This also support a space delimited list of keys. + * `MAKER_ANNOUNCEMENT_KEY` - An optional Maker key, will be used for system wide user generated announcements. If not defined this will fallback to `MAKER_KEY`. A possible use for this is sending important messages and alarms to a CWD that you don't want to send all notification too. This also support a space delimited list of keys. + 4. [Create a recipe](https://ifttt.com/myrecipes/personal/new) or see [more detailed instructions](lib/plugins/maker-setup.md#create-a-recipe) + + Plugins can create custom events, but all events sent to maker will be prefixed with `ns-`. The core events are: + * `ns-event` - This event is sent to the maker service for all alarms and notifications. This is good catch all event for general logging. + * `ns-allclear` - This event is sent to the maker service when an alarm has been ack'd or when the server starts up without triggering any alarms. For example, you could use this event to turn a light to green. + * `ns-info` - Plugins that generate notifications at the info level will cause this event to also be triggered. It will be sent in addition to `ns-event`. + * `ns-warning` - Alarms at the warning level with cause this event to also be triggered. It will be sent in addition to `ns-event`. + * `ns-urgent` - Alarms at the urgent level with cause this event to also be triggered. It will be sent in addition to `ns-event`. + * see the [full list of events](lib/plugins/maker-setup.md#events) + + +### Treatment Profile + Some of the [plugins](#plugins) make use of a treatment profile that can be edited using the Profile Editor, see the link in the Settings drawer on your site. + + Treatment Profile Fields: + + * `timezone` (Time Zone) - time zone local to the patient. *Should be set.* + * `units` (Profile Units) - blood glucose units used in the profile, either "mgdl" or "mmol" + * `dia` (Insulin duration) - value should be the duration of insulin action to use in calculating how much insulin is left active. Defaults to 3 hours. + * `carbs_hr` (Carbs per Hour) - The number of carbs that are processed per hour, for more information see [#DIYPS](http://diyps.org/2014/05/29/determining-your-carbohydrate-absorption-rate-diyps-lessons-learned/). + * `carbratio` (Carb Ratio) - grams per unit of insulin. + * `sens` (Insulin sensitivity) How much one unit of insulin will normally lower blood glucose. + * `basal` The basal rate set on the pump. + * `target_high` - Upper target for correction boluses. + * `target_low` - Lower target for correction boluses. + + Some example profiles are [here](example-profiles.md). ## Setting environment variables Easy to emulate on the commandline: ```bash - echo 'MONGO_CONNECTION="mongodb://sally:sallypass@ds099999.mongolab.com:99999/nightscout"' >> my.env + echo 'MONGO_CONNECTION=mongodb://sally:sallypass@ds099999.mongolab.com:99999/nightscout' >> my.env + echo 'MONGO_COLLECTION=entries' >> my.env ``` From now on you can run using diff --git a/Release.md b/Release.md deleted file mode 100644 index 8e9ba512d0d..00000000000 --- a/Release.md +++ /dev/null @@ -1,15 +0,0 @@ - -v0.4.1 / 2014-09-12 -================== - - * quick hack to prevent mbg records from crashing pebble - * add script to prep release branch - * tweak toolbar and button size/placement - * Merge pull request #128 from nightscout/wip/mbg - * Merge pull request #166 from nightscout/wip/id-rev - * Merge pull request #165 from nightscout/hotfix/pebble-sgv-string - * convert sgv to string - * add ability to easily id git rev-parse HEAD - * Merge branch 'release/0.4.0' into dev - * hack: only consider 'grey' sgv records - * Added searching for MBG data from mongo query. diff --git a/app.js b/app.js new file mode 100644 index 00000000000..fa807b81a5d --- /dev/null +++ b/app.js @@ -0,0 +1,55 @@ + +var express = require('express'); +var compression = require('compression'); +function create (env, ctx) { + /////////////////////////////////////////////////// + // api and json object variables + /////////////////////////////////////////////////// + var api = require('./lib/api/')(env, ctx); + + var app = express(); + var appInfo = env.name + ' ' + env.version; + app.set('title', appInfo); + app.enable('trust proxy'); // Allows req.secure test on heroku https connections. + + app.use(compression({filter: function shouldCompress(req, res) { + //TODO: return false here if we find a condition where we don't want to compress + // fallback to standard filter function + return compression.filter(req, res); + }})); + + //if (env.api_secret) { + // console.log("API_SECRET", env.api_secret); + //} + app.use('/api/v1', api); + + + // pebble data + app.get('/pebble', ctx.pebble); + + // expose swagger.yaml + app.get('/swagger.yaml', function (req, res) { + res.sendFile(__dirname + '/swagger.yaml'); + }); + + //app.get('/package.json', software); + + // define static server + //TODO: JC - changed cache to 1 hour from 30d ays to bypass cache hell until we have a real solution + var staticFiles = express.static(env.static_files, {maxAge: 60 * 60 * 1000}); + + // serve the static content + app.use(staticFiles); + + var bundle = require('./bundle')(); + app.use(bundle); + + // Handle errors with express's errorhandler, to display more readable error messages. + var errorhandler = require('errorhandler'); + //if (process.env.NODE_ENV === 'development') { + app.use(errorhandler()); + //} + return app; +} +module.exports = create; + diff --git a/app.json b/app.json index 77a1a308cd0..c5a6417eed5 100644 --- a/app.json +++ b/app.json @@ -8,17 +8,27 @@ "required": true }, "API_SECRET": { - "description": "REQUIRED: User generated password used for REST API and optional features (12 character minimum).", + "description": "REQUIRED: A secret passphrase that must be at least 12 characters long, required to enable POST and PUT; also required for the Care Portal", "value": "", "required": true }, + "DISPLAY_UNITS": { + "description": "Choices: mg/dl and mmol. Setting to mmol puts the entire server into mmol mode by default, no further settings needed.", + "value": "", + "required": false + }, "ENABLE": { - "description": "Space delimited list of optional features to enable, such as 'careportal'.", + "description": "Used to enable optional features, expects a space delimited list, such as: careportal rawbg iob, see https://github.com/nightscout/cgm-remote-monitor/blob/master/README.md for more info", + "value": "", + "required": false + }, + "DISABLE": { + "description": "Used to disable default features, expects a space delimited list, such as: direction upbat, see https://github.com/nightscout/cgm-remote-monitor/blob/master/README.md for more info", "value": "", "required": false }, "ALARM_TYPES": { - "description": "Nightscout alarm behavior control. Default null value implies 'predict'. For adjustable alarm thresholds (set below), set to 'simple'.", + "description": "Alarm behavior currently 2 alarm types are supported simple and predict, and can be used independently or combined. The simple alarm type only compares the current BG to BG_ thresholds above, the predict alarm type uses highly tuned formula that forecasts where the BG is going based on it's trend. predict DOES NOT currently use any of the BG_* ENV's", "value": "", "required": false }, @@ -51,9 +61,110 @@ "description": "Pushover user key, required for Pushover notifications. Leave blank if not using Pushover.", "value": "", "required": false + }, + "PUSHOVER_ANNOUNCEMENT_KEY": { + "description": "An optional Pushover user/group key, will be used for system wide user generated announcements. If not defined this will fallback to `PUSHOVER_USER_KEY`. A possible use for this is sending important messages and alarms to a CWD that you don't want to send all notification too. This also support a space delimited list of keys. Leave blank if not using Pushover", + "value": "", + "required": false + }, + "CUSTOM_TITLE": { + "description": "Customize the name of the website, usually the name of T1.", + "value": "", + "required": false + }, + "THEME": { + "description": "Possible values default or colors", + "value": "", + "required": false + }, + "SHOW_RAWBG": { + "description": "Possible values always, never or noise", + "value": "", + "required": false + }, + "BRIDGE_USER_NAME": { + "description": "Share bridge - Your user name for the Share service. ENSURE bridge is in ENABLE if you want to use the share bridge", + "value": "", + "required": false + }, + "BRIDGE_PASSWORD": { + "description": "Share bridge - Your password for the Share service. ENSURE bridge is in ENABLE if you want to use the share bridge", + "value": "", + "required": false + }, + "TIME_FORMAT": { + "description": "Browser default time mode valid settings are 12 or 24", + "value": "12", + "required": false + }, + "NIGHT_MODE": { + "description": "Browser defaults to night mode valid settings are on or off", + "value": "off", + "required": false + }, + "SHOW_RAWBG": { + "description": "Browser default raw display mode vaild settings are always never or noise", + "value": "never", + "required": false + }, + "THEME": { + "description": "Browser default theme setting vaild settings are default or colors", + "value": "default", + "required": false + }, + "ALARM_URGENT_HIGH": { + "description": "Browser default urgent high alarm enabled vaild settings are on or off", + "value": "on", + "required": false + }, + "ALARM_HIGH": { + "description": "Browser default high alarm enabled vaild settings are on or off", + "value": "on", + "required": false + }, + "ALARM_LOW": { + "description": "Browser default low alarm enabled vaild settings are on or off", + "value": "on", + "required": false + }, + "ALARM_URGENT_LOW": { + "description": "Browser default urgent low alarm enabled vaild settings are on or off", + "value": "on", + "required": false + }, + "ALARM_TIMEAGO_WARN": { + "description": "Browser default warn after time of last data exceeds ALARM_TIMEAGO_WARN_MINS alarm enabled vaild settings are on or off", + "value": "on", + "required": false + }, + "ALARM_TIMEAGO_WARN_MINS": { + "description": "Browser default minutes since the last reading to trigger a warning", + "value": "15", + "required": false + }, + "ALARM_TIMEAGO_URGENT": { + "description": "Browser default urgent warning after time of last data exceeds ALARM_TIMEAGO_URGENT_MINS alarm enabled vaild settings are on or off", + "value": "on", + "required": false + }, + "ALARM_TIMEAGO_URGENT_MINS": { + "description": "Browser default minutes since last reading to trigger an urgent alarm", + "value": "30", + "required": false + }, + "MAKER_KEY": { + "description": "Maker Key - Set this to your secret key Note for additional info see https://github.com/nightscout/cgm-remote-monitor/blob/dev/README.md#ifttt-maker , maker should be added to enable if you want to use maker, Leave blank if not using maker", + "value": "", + "required": false + }, + "MAKER_ANNOUNCEMENT_KEY": { + "description": "Maker Announcement Key - Set this to your secret key for announcements Note for additional info see https://github.com/nightscout/cgm-remote-monitor/blob/dev/README.md#ifttt-maker , maker should be added to enable if you want to use maker Leave blank if not using maker", + "value": "", + "required": false } }, "addons": [ - "mongolab:sandbox" + "mongolab:sandbox", + "papertrail" ] } diff --git a/bin/post-sgv.sh b/bin/post-sgv.sh new file mode 100755 index 00000000000..9b48e5dfcc9 --- /dev/null +++ b/bin/post-sgv.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# "date": "1413782506964" + +curl -H "Content-Type: application/json" -H "api-secret: $API_SECRET" -XPOST 'http://localhost:1337/api/v1/entries/' -d '{ + "sgv": 100, + "type": "sgv", + "direction": "Flat", + "date": "1415950912800" +}' diff --git a/bower.json b/bower.json index 406470963b2..0511964f203 100644 --- a/bower.json +++ b/bower.json @@ -1,14 +1,13 @@ { "name": "nightscout", - "version": "0.6.2", + "version": "0.8.1", "dependencies": { - "angularjs": "1.3.0-beta.19", - "bootstrap": "~3.2.0", - "d3": "3.4.3", "jquery": "2.1.0", "jQuery-Storage-API": "~1.7.2", "tipsy-jmalonzo": "~1.0.1", - "jsSHA": "~1.5.0" + "jquery-ui": "~1.11.3", + "jquery-flot": "0.8.3", + "swagger-ui": "~2.1.2" }, "resolutions": { "jquery": "2.1.0" diff --git a/bundle/bundle.source.js b/bundle/bundle.source.js new file mode 100644 index 00000000000..d78ecb3c868 --- /dev/null +++ b/bundle/bundle.source.js @@ -0,0 +1,19 @@ +(function () { + + window._ = require('lodash'); + window.$ = window.jQuery = require('jquery'); + window.moment = require('moment-timezone'); + window.Nightscout = window.Nightscout || {}; + + window.Nightscout = { + client: require('../lib/client') + , units: require('../lib/units')() + , plugins: require('../lib/plugins/')().registerClientDefaults() + , report_plugins: require('../lib/report_plugins/')() + , admin_plugins: require('../lib/admin_plugins/')() + }; + + console.info('Nightscout bundle ready'); + +})(); + diff --git a/bundle/index.js b/bundle/index.js new file mode 100644 index 00000000000..4a8cd1563f2 --- /dev/null +++ b/bundle/index.js @@ -0,0 +1,17 @@ +'use strict'; + +var browserify_express = require('browserify-express'); + +function bundle() { + return browserify_express({ + entry: __dirname + '/bundle.source.js', + watch: __dirname + '/../lib/', + mount: '/public/js/bundle.js', + verbose: true, + //minify: true, + bundle_opts: { debug: true }, // enable inline sourcemap on js files + write_file: __dirname + '/bundle.out.js' + }); +} + +module.exports = bundle; \ No newline at end of file diff --git a/deploy.sh b/deploy.sh index 4bc630de4ab..29fbdc49d2b 100755 --- a/deploy.sh +++ b/deploy.sh @@ -99,6 +99,7 @@ selectNodeVersion () { # ---------- echo Handling node.js deployment. +echo "\"$SCM_COMMIT_ID\"" > $DEPLOYMENT_SOURCE/scm-commit-id.json # 1. KuduSync if [[ "$IN_PLACE_DEPLOYMENT" -ne "1" ]]; then diff --git a/env.js b/env.js index 0218d69632a..9d1388b6435 100644 --- a/env.js +++ b/env.js @@ -1,151 +1,169 @@ 'use strict'; -var env = { }; +var _ = require('lodash'); +var fs = require('fs'); var crypto = require('crypto'); var consts = require('./lib/constants'); -var fs = require('fs'); + +var env = { + settings: require('./lib/settings')() +}; + // Module to constrain all config and environment parsing to one spot. +// See the function config ( ) { - /* - * First inspect a bunch of environment variables: - * * PORT - serve http on this port - * * MONGO_CONNECTION, CUSTOMCONNSTR_mongo - mongodb://... uri - * * CUSTOMCONNSTR_mongo_collection - name of mongo collection with "sgv" documents - * * CUSTOMCONNSTR_mongo_settings_collection - name of mongo collection to store configurable settings - * * API_SECRET - if defined, this passphrase is fed to a sha1 hash digest, the hex output is used to create a single-use token for API authorization - * * NIGHTSCOUT_STATIC_FILES - the "base directory" to use for serving - * static files over http. Default value is the included `static` - * directory. + * See README.md for info about all the supported ENV VARs */ - var software = require('./package.json'); - var git = require('git-rev'); - - if (readENV('SCM_GIT_EMAIL') == 'windowsazure' && readENV('ScmType') == 'GitHub') { - git.cwd('/home/site/repository'); - } - if (readENV('SCM_COMMIT_ID')) { - env.head = readENV('SCM_COMMIT_ID'); - } else { - git.short(function record_git_head (head) { - console.log("GIT HEAD", head); - env.head = head; - }); - } - env.version = software.version; - env.name = software.name; - env.DISPLAY_UNITS = readENV('DISPLAY_UNITS', 'mg/dl'); env.PORT = readENV('PORT', 1337); - env.mongo = readENV('MONGO_CONNECTION') || readENV('MONGO') || readENV('MONGOLAB_URI'); - env.mongo_collection = readENV('MONGO_COLLECTION', 'entries'); - env.settings_collection = readENV('MONGO_SETTINGS_COLLECTION', 'settings'); - env.treatments_collection = readENV('MONGO_TREATMENTS_COLLECTION', 'treatments'); - env.devicestatus_collection = readENV('MONGO_DEVICESTATUS_COLLECTION', 'devicestatus'); + env.static_files = readENV('NIGHTSCOUT_STATIC_FILES', __dirname + '/static/'); - env.enable = readENV('ENABLE'); + setSSL(); + setAPISecret(); + setVersion(); + setMongo(); + updateSettings(); + + // require authorization for entering treatments + env.treatments_auth = readENV('TREATMENTS_AUTH',false); + + return env; +} + +function setSSL() { env.SSL_KEY = readENV('SSL_KEY'); env.SSL_CERT = readENV('SSL_CERT'); env.SSL_CA = readENV('SSL_CA'); env.ssl = false; if (env.SSL_KEY && env.SSL_CERT) { env.ssl = { - key: fs.readFileSync(env.SSL_KEY) - , cert: fs.readFileSync(env.SSL_CERT) + key: fs.readFileSync(env.SSL_KEY), cert: fs.readFileSync(env.SSL_CERT) }; if (env.SSL_CA) { env.ca = fs.readFileSync(env.SSL_CA); } } +} - var shasum = crypto.createHash('sha1'); - - ///////////////////////////////////////////////////////////////// - // A little ugly, but we don't want to read the secret into a var - ///////////////////////////////////////////////////////////////// +// A little ugly, but we don't want to read the secret into a var +function setAPISecret() { var useSecret = (readENV('API_SECRET') && readENV('API_SECRET').length > 0); + //TODO: should we clear API_SECRET from process env? env.api_secret = null; // if a passphrase was provided, get the hex digest to mint a single token if (useSecret) { if (readENV('API_SECRET').length < consts.MIN_PASSPHRASE_LENGTH) { - var msg = ["API_SECRET should be at least", consts.MIN_PASSPHRASE_LENGTH, "characters"]; - var err = new Error(msg.join(' ')); - // console.error(err); - throw err; - process.exit(1); + var msg = ['API_SECRET should be at least', consts.MIN_PASSPHRASE_LENGTH, 'characters']; + throw new Error(msg.join(' ')); } + var shasum = crypto.createHash('sha1'); shasum.update(readENV('API_SECRET')); env.api_secret = shasum.digest('hex'); } +} - env.thresholds = { - bg_high: readIntENV('BG_HIGH', 260) - , bg_target_top: readIntENV('BG_TARGET_TOP', 180) - , bg_target_bottom: readIntENV('BG_TARGET_BOTTOM', 80) - , bg_low: readIntENV('BG_LOW', 55) - }; - - //NOTE: using +/- 1 here to make the thresholds look visibly wrong in the UI - // if all thresholds were set to the same value you should see 4 lines stacked right on top of each other - if (env.thresholds.bg_target_bottom >= env.thresholds.bg_target_top) { - console.warn('BG_TARGET_BOTTOM(' + env.thresholds.bg_target_bottom + ') was >= BG_TARGET_TOP(' + env.thresholds.bg_target_top + ')'); - env.thresholds.bg_target_bottom = env.thresholds.bg_target_top - 1; - console.warn('BG_TARGET_BOTTOM is now ' + env.thresholds.bg_target_bottom); - } - - if (env.thresholds.bg_target_top <= env.thresholds.bg_target_bottom) { - console.warn('BG_TARGET_TOP(' + env.thresholds.bg_target_top + ') was <= BG_TARGET_BOTTOM(' + env.thresholds.bg_target_bottom + ')'); - env.thresholds.bg_target_top = env.thresholds.bg_target_bottom + 1; - console.warn('BG_TARGET_TOP is now ' + env.thresholds.bg_target_top); - } +function setVersion() { + var software = require('./package.json'); + var git = require('git-rev'); - if (env.thresholds.bg_low >= env.thresholds.bg_target_bottom) { - console.warn('BG_LOW(' + env.thresholds.bg_low + ') was >= BG_TARGET_BOTTOM(' + env.thresholds.bg_target_bottom + ')'); - env.thresholds.bg_low = env.thresholds.bg_target_bottom - 1; - console.warn('BG_LOW is now ' + env.thresholds.bg_low); + if (readENV('APPSETTING_ScmType') === readENV('ScmType') && readENV('ScmType') === 'GitHub') { + env.head = require('./scm-commit-id.json'); + console.log('SCM COMMIT ID', env.head); + } else { + git.short(function record_git_head(head) { + console.log('GIT HEAD', head); + env.head = head || readENV('SCM_COMMIT_ID') || readENV('COMMIT_HASH', ''); + }); } + env.version = software.version; + env.name = software.name; +} - if (env.thresholds.bg_high <= env.thresholds.bg_target_top) { - console.warn('BG_HIGH(' + env.thresholds.bg_high + ') was <= BG_TARGET_TOP(' + env.thresholds.bg_target_top + ')'); - env.thresholds.bg_high = env.thresholds.bg_target_top + 1; - console.warn('BG_HIGH is now ' + env.thresholds.bg_high); +function setMongo() { + env.mongo = readENV('MONGO_CONNECTION') || readENV('MONGO') || readENV('MONGOLAB_URI'); + env.mongo_collection = readENV('MONGO_COLLECTION', 'entries'); + env.MQTT_MONITOR = readENV('MQTT_MONITOR', null); + if (env.MQTT_MONITOR) { + var hostDbCollection = [env.mongo.split('mongodb://').pop().split('@').pop(), env.mongo_collection].join('/'); + var mongoHash = crypto.createHash('sha1'); + mongoHash.update(hostDbCollection); + //some MQTT servers only allow the client id to be 23 chars + env.mqtt_client_id = mongoHash.digest('base64').substring(0, 23); + console.info('Using Mongo host/db/collection to create the default MQTT client_id', hostDbCollection); + if (env.MQTT_MONITOR.indexOf('?clientId=') === -1) { + console.info('Set MQTT client_id to: ', env.mqtt_client_id); + } else { + console.info('MQTT configured to use a custom client id, it will override the default: ', env.mqtt_client_id); + } } - - //if any of the BG_* thresholds are set, default to "simple" otherwise default to "predict" to preserve current behavior - var thresholdsSet = readIntENV('BG_HIGH') || readIntENV('BG_TARGET_TOP') || readIntENV('BG_TARGET_BOTTOM') || readIntENV('BG_LOW'); - env.alarm_types = readENV('ALARM_TYPES') || (thresholdsSet ? "simple" : "predict"); - - // For pushing notifications to Pushover. - env.pushover_api_token = readENV('PUSHOVER_API_TOKEN'); - env.pushover_user_key = readENV('PUSHOVER_USER_KEY') || readENV('PUSHOVER_GROUP_KEY'); + env.treatments_collection = readENV('MONGO_TREATMENTS_COLLECTION', 'treatments'); + env.profile_collection = readENV('MONGO_PROFILE_COLLECTION', 'profile'); + env.devicestatus_collection = readENV('MONGO_DEVICESTATUS_COLLECTION', 'devicestatus'); // TODO: clean up a bit // Some people prefer to use a json configuration file instead. // This allows a provided json config to override environment variables var DB = require('./database_configuration.json'), DB_URL = DB.url ? DB.url : env.mongo, - DB_COLLECTION = DB.collection ? DB.collection : env.mongo_collection, - DB_SETTINGS_COLLECTION = DB.settings_collection ? DB.settings_collection : env.settings_collection; + DB_COLLECTION = DB.collection ? DB.collection : env.mongo_collection; env.mongo = DB_URL; env.mongo_collection = DB_COLLECTION; - env.settings_collection = DB_SETTINGS_COLLECTION; - env.static_files = readENV('NIGHTSCOUT_STATIC_FILES', __dirname + '/static/'); - - return env; } -function readIntENV(varName, defaultValue) { - return parseInt(readENV(varName)) || defaultValue; +function updateSettings() { + + var envNameOverrides = { + UNITS: 'DISPLAY_UNITS' + }; + + env.settings.eachSettingAsEnv(function settingFromEnv (name) { + var envName = envNameOverrides[name] || name; + return readENV(envName); + }); + + //should always find extended settings last + env.extendedSettings = findExtendedSettings(process.env); } function readENV(varName, defaultValue) { - //for some reason Azure uses this prefix, maybe there is a good reason - var value = process.env['CUSTOMCONNSTR_' + varName] - || process.env['CUSTOMCONNSTR_' + varName.toLowerCase()] - || process.env[varName] - || process.env[varName.toLowerCase()]; + //for some reason Azure uses this prefix, maybe there is a good reason + var value = process.env['CUSTOMCONNSTR_' + varName] + || process.env['CUSTOMCONNSTR_' + varName.toLowerCase()] + || process.env[varName] + || process.env[varName.toLowerCase()]; - return value || defaultValue; + if (typeof value === 'string' && value.toLowerCase() === 'on') { value = true; } + if (typeof value === 'string' && value.toLowerCase() === 'off') { value = false; } + + return value != null ? value : defaultValue; +} + +function findExtendedSettings (envs) { + var extended = {}; + + function normalizeEnv (key) { + return key.toUpperCase().replace('CUSTOMCONNSTR_', ''); + } + + _.each(env.settings.enable, function eachEnable(enable) { + if (_.trim(enable)) { + _.forIn(envs, function eachEnvPair (value, key) { + var env = normalizeEnv(key); + if (_.startsWith(env, enable.toUpperCase() + '_')) { + var split = env.indexOf('_'); + if (split > -1 && split <= env.length) { + var exts = extended[enable] || {}; + extended[enable] = exts; + var ext = _.camelCase(env.substring(split + 1).toLowerCase()); + if (!isNaN(value)) { value = Number(value); } + exts[ext] = value; + } + } + }); + } + }); + return extended; } module.exports = config; diff --git a/example-profiles.md b/example-profiles.md new file mode 100644 index 00000000000..717bc1df9dc --- /dev/null +++ b/example-profiles.md @@ -0,0 +1,77 @@ + + +**Table of Contents** + +- [Example Profiles](#example-profiles) + - [Simple profile](#simple-profile) + - [Profile can also use time periods for any field, for example:](#profile-can-also-use-time-periods-for-any-field-for-example) + + + +#Example Profiles + +These are only examples, make sure you update all fields to fit your needs + +##Simple profile + ```json + { + "dia": 3, + "carbs_hr": 20, + "carbratio": 30, + "sens": 100, + "basal": 0.125, + "target_low": 100, + "target_high": 120 + } + ``` + +##Profile can also use time periods for any field, for example: + + ```json + { + "carbratio": [ + { + "time": "00:00", + "value": 30 + }, + { + "time": "06:00", + "value": 25 + }, + { + "time": "14:00", + "value": 28 + } + ], + "basal": [ + { + "time": "00:00", + "value": 0.175 + }, + { + "time": "02:30", + "value": 0.125 + }, + { + "time": "05:00", + "value": 0.075 + }, + { + "time": "08:00", + "value": 0.100 + }, + { + "time": "14:00", + "value": 0.125 + }, + { + "time": "20:00", + "value": 0.175 + }, + { + "time": "22:00", + "value": 0.200 + } + ] + } + ``` diff --git a/lib/admin_plugins/cleanstatusdb.js b/lib/admin_plugins/cleanstatusdb.js new file mode 100644 index 00000000000..86e61e987f8 --- /dev/null +++ b/lib/admin_plugins/cleanstatusdb.js @@ -0,0 +1,70 @@ +'use strict'; + +var cleanstatusdb = { + name: 'cleanstatusdb' + , label: 'Clean Mongo status database' + , pluginType: 'admin' +}; + +function init() { + return cleanstatusdb; +} + +module.exports = init; + +cleanstatusdb.actions = [ + { + name: 'Delete all documents from devicestatus collection' + , description: 'This task removes all documents from devicestatus collection. Useful when uploader battery status is not properly updated.' + , buttonLabel: 'Delete all documents' + , confirmText: 'Delete all documents from devicestatus collection?' + } + ]; + +cleanstatusdb.actions[0].init = function init(client, callback) { + var translate = client.translate; + var $status = $('#admin_' + cleanstatusdb.name + '_0_status'); + + $status.hide().text(translate('Loading database ...')).fadeIn('slow'); + $.ajax('/api/v1/devicestatus.json?count=500', { + success: function (records) { + var recs = (records.length === 500 ? '500+' : records.length); + $status.hide().text(translate('Database contains %1 records',{ params: [recs] })).fadeIn('slow'); + }, + error: function () { + $status.hide().text(translate('Error loading database')).fadeIn('slow'); + } + }).done(function () { if (callback) { callback(); } }); +}; + +cleanstatusdb.actions[0].code = function deleteRecords(client, callback) { + var translate = client.translate; + var $status = $('#admin_' + cleanstatusdb.name + '_0_status'); + + if (!client.hashauth.isAuthenticated()) { + alert(translate('Your device is not authenticated yet')); + if (callback) { + callback(); + } + return; + }; + + $status.hide().text(translate('Deleting records ...')).fadeIn('slow'); + $.ajax({ + method: 'DELETE' + , url: '/api/v1/devicestatus/*' + , headers: { + 'api-secret': client.hashauth.hash() + } + }).done(function success () { + $status.hide().text(translate('All records removed ...')).fadeIn('slow'); + if (callback) { + callback(); + } + }).fail(function fail() { + $status.hide().text(translate('Error')).fadeIn('slow'); + if (callback) { + callback(); + } + }); +}; diff --git a/lib/admin_plugins/futureitems.js b/lib/admin_plugins/futureitems.js new file mode 100644 index 00000000000..ab888aff0a6 --- /dev/null +++ b/lib/admin_plugins/futureitems.js @@ -0,0 +1,172 @@ +'use strict'; + +var futureitems = { + name: 'futureitems' + , label: 'Remove future items from mongo database' + , pluginType: 'admin' +}; + +function init() { + return futureitems; +} + +module.exports = init; + +futureitems.actions = [ + { + name: 'Find and remove treatments in the future' + , description: 'This task find and remove treatments in the future.' + , buttonLabel: 'Remove treatments in the future' + } + , { + name: 'Find and remove entries in the future' + , description: 'This task find and remove CGM data in the future created by uploader with wrong date/time.' + , buttonLabel: 'Remove entries in the future' + } + ]; + +futureitems.actions[0].init = function init(client, callback) { + var translate = client.translate; + var $status = $('#admin_' + futureitems.name + '_0_status'); + + function valueOrEmpty (value) { + return value ? value : ''; + } + + function showOneTreatment (tr, table) { + table.append($('