diff --git a/640gAndroidUploader.iml b/640gAndroidUploader.iml
index 21ad486e1dacdc31dfcf484957078ef9fbc29307..dd11c422309587fbfb2c40dcfbc1c5c64bb0d573 100644
--- a/640gAndroidUploader.iml
+++ b/640gAndroidUploader.iml
@@ -13,7 +13,7 @@
     <content url="file://$MODULE_DIR$">
       <excludeFolder url="file://$MODULE_DIR$/.gradle" />
     </content>
-    <orderEntry type="inheritedJdk" />
+    <orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
     <orderEntry type="sourceFolder" forTests="false" />
   </component>
 </module>
\ No newline at end of file
diff --git a/README.md b/README.md
index 1b749f09ecacdb0af5e8e4f2ced8707c9a370492..599ec520263dbf85f894c94c8bc2c130f1d7a9e9 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,27 @@ This is an Android app to upload data from a MiniMed 640G insulin pump to a Nigh
 <a target="blank" href="https://raw.githubusercontent.com/wiki/pazaan/640gAndroidUploader/images/kit-in-case-2.jpg"><img src="https://raw.githubusercontent.com/wiki/pazaan/640gAndroidUploader/images/kit-in-case-2.jpg" width="200"></a>
 <br/><br/>
 
+#### Development - getting started
+
+ - Install [Android Studio](https://developer.android.com/studio/index.html)
+ - Install [Fabric Plugin](https://fabric.io), enable Crashlytics, this should create `app/fabric.properties` with your fabric apiSecret and also add a fabric key to `app/src/AndroidManifest.xml`
+
+   ```
+   <meta-data
+       android:name="io.fabric.ApiKey"
+       android:value="YOUR-FABRIC-KEY" />
+   ```
+
+   (**take care not to commit this change**)
+ - Create a [BugFender](https://app.bugfender.com) account, create `app/bugfender.properties` and populate with
+
+   ```
+   apiKey=YOUR-BUGFENDER-KEY
+   ```
+ - Set up a virtual device in the AVD manager or connect an Android phone with USB debugging enabled.
+
+ - Use one of the run configurations, eg `installDebug`
+ 
 #### App Credits
 * Based on https://github.com/arbox0/MedtronicUploader *(though the internals are completely changed for the 640G)*
 * Uses the [android-service-example](https://code.launchpad.net/~binwiederhier/+junk/android-service-example) by Philipp C. Heckel
diff --git a/app/app.iml b/app/app.iml
deleted file mode 100644
index a296ac55fc53b536763a4cfd375a0802b8956039..0000000000000000000000000000000000000000
--- a/app/app.iml
+++ /dev/null
@@ -1,157 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="640gAndroidUploader" external.system.module.version="0.3.0-SNAPSHOT" type="JAVA_MODULE" version="4">
-  <component name="FacetManager">
-    <facet type="android-gradle" name="Android-Gradle">
-      <configuration>
-        <option name="GRADLE_PROJECT_PATH" value=":app" />
-      </configuration>
-    </facet>
-    <facet type="android" name="Android">
-      <configuration>
-        <option name="SELECTED_BUILD_VARIANT" value="debug" />
-        <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
-        <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
-        <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
-        <afterSyncTasks>
-          <task>generateDebugSources</task>
-        </afterSyncTasks>
-        <option name="ALLOW_USER_CONFIGURATION" value="false" />
-        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
-        <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
-        <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
-        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
-      </configuration>
-    </facet>
-  </component>
-  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
-    <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
-    <output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
-    <exclude-output />
-    <content url="file://$MODULE_DIR$">
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/fabric/res/debug" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/jni" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/animated-vector-drawable/23.4.0/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.4.0/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/23.4.0/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/23.4.0/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.4.0/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-vector-drawable/23.4.0/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.bugfender.sdk/android/0.4/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.crashlytics.sdk.android/answers/1.3.6/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.crashlytics.sdk.android/beta/1.1.4/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.crashlytics.sdk.android/crashlytics-core/2.3.8/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.crashlytics.sdk.android/crashlytics/2.5.5/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.getkeepsafe.relinker/relinker/1.2.1/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.mikepenz/fastadapter/1.5.2/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.mikepenz/google-material-typeface/2.2.0.1.original/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.mikepenz/iconics-core/2.6.0/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.mikepenz/materialdrawer/5.2.9/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.mikepenz/materialize/0.8.8/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/io.fabric.sdk.android/fabric/1.3.10/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/io.realm/realm-android-library/1.0.0/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/uk.co.chrisjenx/calligraphy/2.2.0/jars" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
-      <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
-      <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
-    </content>
-    <orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
-    <orderEntry type="sourceFolder" forTests="false" />
-    <orderEntry type="library" exported="" name="okio-1.8.0" level="project" />
-    <orderEntry type="library" exported="" name="support-annotations-23.4.0" level="project" />
-    <orderEntry type="library" exported="" name="google-material-typeface-2.2.0.1.original" level="project" />
-    <orderEntry type="library" exported="" name="MPAndroidChart-v2.2.5" level="project" />
-    <orderEntry type="library" exported="" name="okhttp-2.4.0" level="project" />
-    <orderEntry type="library" exported="" name="relinker-1.2.1" level="project" />
-    <orderEntry type="library" exported="" name="retrofit-1.9.0" level="project" />
-    <orderEntry type="library" exported="" name="realm-annotations-1.0.0" level="project" />
-    <orderEntry type="library" exported="" name="animated-vector-drawable-23.4.0" level="project" />
-    <orderEntry type="library" exported="" name="commons-lang3-3.4" level="project" />
-    <orderEntry type="library" exported="" name="support-v4-23.4.0" level="project" />
-    <orderEntry type="library" exported="" name="android-0.4" level="project" />
-    <orderEntry type="library" exported="" name="recyclerview-v7-23.4.0" level="project" />
-    <orderEntry type="library" exported="" name="slf4j-api-1.7.2" level="project" />
-    <orderEntry type="library" exported="" name="support-vector-drawable-23.4.0" level="project" />
-    <orderEntry type="library" exported="" name="materialize-0.8.8" level="project" />
-    <orderEntry type="library" exported="" name="realm-android-library-1.0.0" level="project" />
-    <orderEntry type="library" exported="" name="appcompat-v7-23.4.0" level="project" />
-    <orderEntry type="library" exported="" name="fabric-1.3.10" level="project" />
-    <orderEntry type="library" exported="" name="design-23.4.0" level="project" />
-    <orderEntry type="library" exported="" name="crashlytics-2.5.5" level="project" />
-    <orderEntry type="library" exported="" name="crashlytics-core-2.3.8" level="project" />
-    <orderEntry type="library" exported="" name="beta-1.1.4" level="project" />
-    <orderEntry type="library" exported="" name="fastadapter-1.5.2" level="project" />
-    <orderEntry type="library" exported="" name="retrofit-2.1.0" level="project" />
-    <orderEntry type="library" exported="" name="gson-2.7" level="project" />
-    <orderEntry type="library" exported="" name="converter-gson-2.1.0" level="project" />
-    <orderEntry type="library" exported="" name="materialdrawer-5.2.9" level="project" />
-    <orderEntry type="library" exported="" name="calligraphy-2.2.0" level="project" />
-    <orderEntry type="library" exported="" name="okhttp-3.3.0" level="project" />
-    <orderEntry type="library" exported="" name="answers-1.3.6" level="project" />
-    <orderEntry type="library" exported="" name="iconics-core-2.6.0" level="project" />
-    <orderEntry type="library" exported="" name="org.apache.http.legacy-android-23" level="project" />
-  </component>
-</module>
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 0fa95d999428d38e08ad5f488c7cb844285ca4c7..321863d615cb36775b630d58e4edd1721a5f7a06 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,3 +1,5 @@
+import org.ajoberstar.grgit.Grgit
+
 buildscript {
     repositories {
         maven { url 'https://maven.fabric.io/public' }
@@ -5,6 +7,8 @@ buildscript {
 
     dependencies {
         classpath 'io.fabric.tools:gradle:1.21.6'
+        classpath 'io.realm:realm-gradle-plugin:1.1.1'
+        classpath 'org.ajoberstar:grgit:1.5.0'
     }
 }
 plugins {
@@ -12,24 +16,41 @@ plugins {
 }
 
 apply plugin: 'com.android.application'
-apply plugin: 'io.fabric'
-apply plugin: 'realm-android'
 
 repositories {
     maven { url 'https://maven.fabric.io/public' }
     maven { url "https://jitpack.io" }
 }
 
+apply plugin: 'io.fabric'
+apply plugin: 'realm-android'
 
 def gitVersion() {
-    def process = ['sh', '-c', 'git tag -l | grep -c ".*" -'].execute().text.trim()
-    return process.toInteger() + 1
+    // current dir is <your proj>/app, so it's likely that all your git repo files are in the dir
+    // above.
+    ext.repo = Grgit.open(project.file('..'))
+
+    // should result in the same value as running
+    // git tag -l | wc -l or git tag -l | grep -c ".*" -
+    def numOfTags = ext.repo.tag.list().size()
+    return numOfTags
+}
+
+def gitCommitId() {
+    //def process = ['sh', '-c', 'git tag -l | grep -c ".*" -'].execute().text.trim()
+    //return process.toInteger() + 1
+    //return 42
+    // current dir is <your proj>/app, so it's likely that all your git repo files are in the dir
+    // above.
+    ext.repo = Grgit.open(project.file('..'))
+
+    return ext.repo.log().first().id.substring(0, 7)
 }
 
-def getBugfenderApiKey(){
+def getBugfenderApiKey() {
     Properties properties = new Properties()
     properties.load(new FileInputStream("app/bugfender.properties"))
-    return "\"" + properties.getProperty("apiKey", "") +"\""
+    return "\"" + properties.getProperty("apiKey", "") + "\""
 }
 
 android {
@@ -46,7 +67,7 @@ android {
         applicationId "info.nightscout.android"
         minSdkVersion 14
         targetSdkVersion 23
-        versionName project.properties['version']
+        versionName project.properties['version'] + "/" + gitCommitId()
         versionCode gitVersion()
         buildConfigField "String", "BUGFENDER_API_KEY", getBugfenderApiKey()
     }
@@ -74,7 +95,7 @@ task signRelease << {
             '-digestalg',
             'SHA1',
             '-keystore',
-            '/Users/lgoedhart/keystores/nightscout_android.jks',
+            '/Users/lennart/keystores/nightscout_android.jks',
             'app/build/outputs/apk/app-release-unsigned.apk',
             'nightscoutandroidkey'
     ]
@@ -94,7 +115,7 @@ task signRelease << {
 
 task zipalignRelease << {
     def command = [
-            '/Users/lgoedhart/Library/Android/sdk/build-tools/23.0.3/zipalign',
+            '/Users/lennart/Library/Android/sdk/build-tools/23.0.3/zipalign',
             '-v',
             '4',
             'app/build/outputs/apk/app-release-unsigned.apk',
@@ -116,14 +137,14 @@ task zipalignRelease << {
 
 release {
     tagTemplate = 'v${version}'
-    buildTasks = ['assembleRelease']
+    buildTasks = ['build']
     beforeReleaseBuild.dependsOn 'clean'
     afterReleaseBuild.dependsOn 'signRelease', 'zipalignRelease'
 }
 
 dependencies {
     compile files('libs/slf4j-api-1.7.2.jar')
-    compile('com.crashlytics.sdk.android:crashlytics:2.5.5@aar') {
+    compile('com.crashlytics.sdk.android:crashlytics:2.6.5@aar') {
         transitive = true;
     }
     compile('com.mikepenz:materialdrawer:5.2.9@aar') {
@@ -133,8 +154,9 @@ dependencies {
     compile 'org.apache.commons:commons-lang3:3.4'
     compile 'com.mikepenz:google-material-typeface:2.2.0.1.original@aar'
     compile 'uk.co.chrisjenx:calligraphy:2.2.0'
-    compile 'com.bugfender.sdk:android:0.4'
-    compile 'com.github.PhilJay:MPAndroidChart:v2.2.5'
+    compile 'com.bugfender.sdk:android:0.6.2'
+    compile 'com.github.PhilJay:MPAndroidChart:v3.0.0-beta1'
+    compile 'com.github.PhilJay:MPAndroidChart-Realm:v1.1.0@aar'
     compile 'com.android.support:support-v4:23.4.0'
     compile 'com.google.code.gson:gson:2.7'
     compile 'com.squareup.retrofit2:retrofit:2.1.0'
diff --git a/app/gradle.properties b/app/gradle.properties
index 0db303f3a5e5d10435a82acd862bec04bc146452..1dd0e26d2b59d84aa9b32ab72e3c5b99065efabe 100644
--- a/app/gradle.properties
+++ b/app/gradle.properties
@@ -1 +1 @@
-version=0.4.0-SNAPSHOT
+version=0.5.0-SNAPSHOT
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b875e849aaf3675d64cbee090851630aa761a929..f7d7185b04799440fc232b3ffd10ef632e76220c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,8 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     package="info.nightscout.android">
 
-    <uses-sdk android:maxSdkVersion="23" />
+    <uses-sdk android:minSdkVersion="14" android:maxSdkVersion="23"
+        tools:overrideLibrary="com.github.mikephil.charting.data.realm"/>
 
     <uses-feature android:name="android.hardware.usb.host" />
 
@@ -16,6 +18,9 @@
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.USB_PERMISSION" />
 
+    <!-- allow to disable battery optimization -->
+    <uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
+
     <application
         android:name=".UploaderApplication"
         android:allowBackup="true"
@@ -24,6 +29,7 @@
         android:supportsRtl="true"
         android:theme="@style/AppTheme">
 
+
         <!-- I have set screenOrientation to "portrait" to avoid the restart of AsyncTasks when you rotate the phone -->
         <activity
             android:name=".medtronic.MainActivity"
@@ -31,6 +37,7 @@
             android:label="@string/app_name"
             android:launchMode="singleTask"
             android:screenOrientation="portrait">
+
             <intent-filter android:icon="@drawable/ic_launcher">
                 <category android:name="android.intent.category.DEFAULT" />
 
@@ -61,15 +68,22 @@
             android:label="@string/title_activity_login"
             android:theme="@style/SettingsTheme" />
 
+        <activity android:name=".medtronic.StatusActivity" />
+
         <service
             android:name=".upload.nightscout.NightscoutUploadIntentService"
             android:icon="@drawable/ic_launcher" />
-
+        <service
+            android:name=".xdrip_plus.XDripPlusUploadIntentService"
+            android:icon="@drawable/ic_launcher" />
         <service
             android:name=".medtronic.service.MedtronicCnlIntentService"
             android:icon="@drawable/ic_launcher" />
 
-        <activity android:name=".medtronic.StatusActivity"></activity>
+        <receiver android:name=".medtronic.service.MedtronicCnlAlarmReceiver" />
+        <receiver android:name=".upload.nightscout.NightscoutUploadReceiver" />
+        <receiver android:name=".xdrip_plus.XDripPlusUploadReceiver" />
+
     </application>
 
 </manifest>
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/android/USB/UsbHidDriver.java b/app/src/main/java/info/nightscout/android/USB/UsbHidDriver.java
index ea63fa44d6f6f2019bfae1345156a09cd1a114ab..f7f1007adf8cf3c7bdc22e95a94a30cf1f0b481f 100644
--- a/app/src/main/java/info/nightscout/android/USB/UsbHidDriver.java
+++ b/app/src/main/java/info/nightscout/android/USB/UsbHidDriver.java
@@ -9,8 +9,6 @@ import android.util.Log;
 
 import java.io.IOException;
 
-//import com.hoho.android.usbserial.driver.UsbId;
-
 /**
  * USB HID Driver implementation.
  *
@@ -89,14 +87,10 @@ public class UsbHidDriver extends CommonUsbDriver {
             int readAmt = Math.min(dest.length, mReadBuffer.length);
             numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt,
                     timeoutMillis);
-            if (numBytesRead < 0) {
-                // This sucks: we get -1 on timeout, not 0 as preferred.
-                // We *should* use UsbRequest, except it has a bug/api oversight
-                // where there is no way to determine the number of bytes read
-                // in response :\ -- http://b.android.com/28023
-                return 0;
+            if (numBytesRead > 0) {
+                System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
             }
-            System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
+            
             mConnection.releaseInterface(mInterface);
         }
         return numBytesRead;
diff --git a/app/src/main/java/info/nightscout/android/UploaderApplication.java b/app/src/main/java/info/nightscout/android/UploaderApplication.java
index 13fe3d456d7a3afbd0368ca2e10a47d1dbd5ec53..aa1944885e68507b24370f9bf2f2b75fe774bda1 100644
--- a/app/src/main/java/info/nightscout/android/UploaderApplication.java
+++ b/app/src/main/java/info/nightscout/android/UploaderApplication.java
@@ -32,7 +32,7 @@ public class UploaderApplication extends Application {
             Fabric.with(this, new Crashlytics());
         }
         if (prefs.getBoolean(getString(R.string.preferences_enable_answers), true)) {
-            Fabric.with(this, new Answers());
+            Fabric.with(this, new Answers(), new Crashlytics());
         }
 
         if (prefs.getBoolean(getString(R.string.preferences_enable_remote_logcat), false)) {
@@ -41,7 +41,10 @@ public class UploaderApplication extends Application {
             Bugfender.setDeviceString("NightscoutURL", prefs.getString(getString(R.string.preference_nightscout_url), "Not set"));
         }
 
-        RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(this).build();
+        RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(this)
+                .deleteRealmIfMigrationNeeded()
+                .build();
+
         Realm.setDefaultConfiguration(realmConfiguration);
     }
 }
diff --git a/app/src/main/java/info/nightscout/android/medtronic/GetHmacAndKeyActivity.java b/app/src/main/java/info/nightscout/android/medtronic/GetHmacAndKeyActivity.java
index 64c30b3aea050802499e70eac5b49b00e373fbea..dfd43740a7f4ce86fafde82fc746d661476fd109 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/GetHmacAndKeyActivity.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/GetHmacAndKeyActivity.java
@@ -62,19 +62,8 @@ import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper;
  */
 public class GetHmacAndKeyActivity extends AppCompatActivity implements LoaderCallbacks<Cursor> {
 
-    /**
-     * Keep track of the login task to ensure we can cancel it if requested.
-     */
-    // TODO - Replace with Rx.Java
-    private GetHmacAndKey mHmacAndKeyTask = null;
-
     // UI references.
-    private EditText mUsernameView;
-    private EditText mPasswordView;
-    private View mProgressView;
-    private View mLoginFormView;
     private TextView mRegisteredStickView;
-    private MenuItem mLoginMenuItem;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -82,25 +71,8 @@ public class GetHmacAndKeyActivity extends AppCompatActivity implements LoaderCa
         setContentView(R.layout.activity_login);
 
         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-        getSupportActionBar().setTitle("Register USB");
-
-        // Set up the login form.
-        mUsernameView = (EditText) findViewById(R.id.username);
+        getSupportActionBar().setTitle("Registered Devices");
 
-        mPasswordView = (EditText) findViewById(R.id.password);
-        mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
-            @Override
-            public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
-                if (id == EditorInfo.IME_ACTION_DONE) {
-                    attemptLogin();
-                    return true;
-                }
-                return false;
-            }
-        });
-
-        mLoginFormView = findViewById(R.id.login_form);
-        mProgressView = findViewById(R.id.login_progress);
         mRegisteredStickView = (TextView) findViewById(R.id.registered_usb_devices);
 
         showRegisteredSticks();
@@ -108,21 +80,12 @@ public class GetHmacAndKeyActivity extends AppCompatActivity implements LoaderCa
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
-        MenuInflater inflater = getMenuInflater();
-        inflater.inflate(R.menu.menu_register_usb, menu);
-
-        mLoginMenuItem = menu.findItem(R.id.action_menu_login);
-        mLoginMenuItem.setIcon(new IconicsDrawable(this, GoogleMaterial.Icon.gmd_cloud_download).color(Color.WHITE).actionBar());
-
         return true;
     }
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
-            case R.id.action_menu_login:
-                attemptLogin();
-                break;
             case android.R.id.home:
                 finish();
                 break;
@@ -135,96 +98,12 @@ public class GetHmacAndKeyActivity extends AppCompatActivity implements LoaderCa
         super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
     }
 
-    /**
-     * Attempts to sign in or register the account specified by the login form.
-     * If there are form errors (invalid username, missing fields, etc.), the
-     * errors are presented and no actual login attempt is made.
-     */
-    private void attemptLogin() {
-        if (mHmacAndKeyTask != null || !checkOnline("Please connect to the Internet", "You must be online to register your USB stick.")) {
-            return;
-        }
-
-        // Reset errors.
-        mUsernameView.setError(null);
-        mPasswordView.setError(null);
-
-        // Store values at the time of the login attempt.
-        String username = mUsernameView.getText().toString();
-        String password = mPasswordView.getText().toString();
-
-        boolean cancel = false;
-        View focusView = null;
-
-        // Check for a valid password, if the user entered one.
-        if (TextUtils.isEmpty(password)) {
-            mPasswordView.setError(getString(R.string.error_invalid_password));
-            focusView = mPasswordView;
-            cancel = true;
-        }
-
-        // Check for a valid username address.
-        if (TextUtils.isEmpty(username)) {
-            mUsernameView.setError(getString(R.string.error_field_required));
-            focusView = mUsernameView;
-            cancel = true;
-        }
-
-        if (cancel) {
-            // There was an error; don't attempt login and focus the first
-            // form field with an error.
-            focusView.requestFocus();
-        } else {
-            // Show a progress spinner, and kick off a background task to
-            // perform the user login attempt.
-            showProgress(true);
-            mHmacAndKeyTask = new GetHmacAndKey(username, password);
-            mHmacAndKeyTask.execute((Void) null);
-        }
-    }
-
-    /**
-     * Shows the progress UI and hides the login form.
-     */
-    @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
-    private void showProgress(final boolean show) {
-        // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
-        // for very easy animations. If available, use these APIs to fade-in
-        // the progress spinner.
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
-            int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
-
-            mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
-            mLoginFormView.animate().setDuration(shortAnimTime).alpha(
-                    show ? 0 : 1).setListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
-                }
-            });
-
-            mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
-            mProgressView.animate().setDuration(shortAnimTime).alpha(
-                    show ? 1 : 0).setListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
-                }
-            });
-        } else {
-            // The ViewPropertyAnimator APIs are not available, so simply show
-            // and hide the relevant UI components.
-            mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
-            mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
-        }
-    }
-
     private void showRegisteredSticks() {
         Realm realm = Realm.getDefaultInstance();
 
         RealmResults<ContourNextLinkInfo> results = realm.where(ContourNextLinkInfo.class).findAll();
 
-        String deviceTableHtml = "<big><b>Registered Devices</b></big><br/>";
+        String deviceTableHtml = "";
 
         for (ContourNextLinkInfo info : results) {
             String longSerial = info.getSerialNumber();
@@ -236,29 +115,6 @@ public class GetHmacAndKeyActivity extends AppCompatActivity implements LoaderCa
         mRegisteredStickView.setText(Html.fromHtml(deviceTableHtml));
     }
 
-    private boolean checkOnline(String title, String message) {
-        ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
-        NetworkInfo netInfo = cm.getActiveNetworkInfo();
-
-        boolean isOnline = (netInfo != null && netInfo.isConnectedOrConnecting());
-
-        if (!isOnline) {
-            new AlertDialog.Builder(this, R.style.AppTheme)
-                    .setTitle(title)
-                    .setMessage(message)
-                    .setCancelable(false)
-                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-                        public void onClick(DialogInterface dialog, int which) {
-                            dialog.dismiss();
-                        }
-                    })
-                    .setIcon(android.R.drawable.ic_dialog_alert)
-                    .show();
-        }
-
-        return isOnline;
-    }
-
     @Override
     public Loader<Cursor> onCreateLoader(int id, Bundle args) {
         return null;
@@ -271,116 +127,4 @@ public class GetHmacAndKeyActivity extends AppCompatActivity implements LoaderCa
     @Override
     public void onLoaderReset(Loader<Cursor> loader) {
     }
-
-    /**
-     * Represents an asynchronous login/registration task used to authenticate
-     * the user.
-     */
-    public class GetHmacAndKey extends AsyncTask<Void, Void, Boolean> {
-
-        private final String mUsername;
-        private final String mPassword;
-
-        GetHmacAndKey(String username, String password) {
-            mUsername = username;
-            mPassword = password;
-        }
-
-        @Override
-        protected Boolean doInBackground(final Void... params) {
-            try {
-                DefaultHttpClient client = new DefaultHttpClient();
-                HttpPost loginPost = new HttpPost("https://carelink.minimed.eu/patient/j_security_check");
-                List<NameValuePair> nameValuePairs = new ArrayList<>();
-                nameValuePairs.add(new BasicNameValuePair("j_username", mUsername));
-                nameValuePairs.add(new BasicNameValuePair("j_password", mPassword));
-                nameValuePairs.add(new BasicNameValuePair("j_character_encoding", "UTF-8"));
-                loginPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));
-                HttpResponse response = client.execute(loginPost);
-
-                if (response.getStatusLine().getStatusCode() == 200) {
-                    // Get the HMAC/keys for every serial we have seen
-                    Realm realm = Realm.getDefaultInstance();
-
-                    RealmResults<ContourNextLinkInfo> results = realm.where(ContourNextLinkInfo.class).findAll();
-                    for (ContourNextLinkInfo info : results) {
-                        String longSerial = info.getSerialNumber();
-
-                        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-                        ObjectOutputStream hmacRequest = new ObjectOutputStream(buffer);
-                        hmacRequest.writeInt(0x1c);
-                        hmacRequest.writeObject(longSerial.replaceAll("\\d+-", ""));
-
-                        HttpPost hmacPost = new HttpPost("https://carelink.minimed.eu/patient/secure/SnapshotServer/");
-                        hmacPost.setEntity(new ByteArrayEntity(buffer.toByteArray()));
-                        hmacPost.setHeader("Content-type", "application/octet-stream");
-                        response = client.execute(hmacPost);
-
-                        ByteArrayInputStream inputBuffer = new ByteArrayInputStream(EntityUtils.toByteArray(response.getEntity()));
-                        ObjectInputStream hmacResponse = new ObjectInputStream(inputBuffer);
-                        byte[] hmacBytes = (byte[]) hmacResponse.readObject();
-                        ArrayUtils.reverse(hmacBytes);
-                        String hmac = MessageUtils.byteArrayToHexString(hmacBytes);
-
-                        buffer.reset();
-                        inputBuffer.reset();
-
-                        ObjectOutputStream keyRequest = new ObjectOutputStream(buffer);
-                        keyRequest.writeInt(0x1f);
-                        keyRequest.writeObject(longSerial);
-
-                        HttpPost keyPost = new HttpPost("https://carelink.minimed.eu/patient/secure/SnapshotServer/");
-                        keyPost.setEntity(new ByteArrayEntity(buffer.toByteArray()));
-                        keyPost.setHeader("Content-type", "application/octet-stream");
-                        response = client.execute(keyPost);
-
-                        inputBuffer = new ByteArrayInputStream(EntityUtils.toByteArray(response.getEntity()));
-                        ObjectInputStream keyResponse = new ObjectInputStream(inputBuffer);
-                        keyResponse.readInt(); // Throw away the first int. Not sure what it does
-                        String key = MessageUtils.byteArrayToHexString((byte[]) keyResponse.readObject());
-
-                        realm.beginTransaction();
-                        info.setHmac(hmac);
-                        info.setKey(key);
-                        realm.commitTransaction();
-                    }
-
-                    return true;
-                }
-
-            } catch (ClientProtocolException e) {
-                return false;
-            } catch (IOException e) {
-                return false;
-            } catch (ClassNotFoundException e) {
-                return false;
-            }
-
-            return false;
-        }
-
-        @Override
-        protected void onPostExecute(final Boolean success) {
-            mHmacAndKeyTask = null;
-
-            if (success) {
-                showRegisteredSticks();
-                mLoginMenuItem.setVisible(false);
-                mLoginFormView.setVisibility(View.GONE);
-                mProgressView.setVisibility(View.GONE);
-                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
-                imm.hideSoftInputFromWindow(mLoginFormView.getWindowToken(), 0);
-            } else {
-                showProgress(false);
-                mPasswordView.setError(getString(R.string.error_incorrect_password));
-                mPasswordView.requestFocus();
-            }
-        }
-
-        @Override
-        protected void onCancelled() {
-            mHmacAndKeyTask = null;
-            showProgress(false);
-        }
-    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java b/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java
index 9c632ee91d22a1c65128f9215a2e42ae3eb1c11c..d1249934460aebef1968d8ab832c1dcdbb04f5f3 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java
@@ -3,6 +3,7 @@ package info.nightscout.android.medtronic;
 import android.app.AlarmManager;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -12,10 +13,14 @@ import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
+import android.net.Uri;
 import android.os.BatteryManager;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.PowerManager;
 import android.preference.PreferenceManager;
+import android.provider.Settings;
 import android.support.v4.app.TaskStackBuilder;
 import android.support.v4.content.LocalBroadcastManager;
 import android.support.v7.app.AlertDialog;
@@ -32,7 +37,8 @@ import android.view.View;
 import android.widget.TextView;
 import android.widget.TextView.BufferType;
 
-import com.github.mikephil.charting.data.realm.implementation.RealmLineData;
+import com.github.mikephil.charting.charts.LineChart;
+import com.github.mikephil.charting.data.LineData;
 import com.github.mikephil.charting.data.realm.implementation.RealmLineDataSet;
 import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
 import com.github.mikephil.charting.utils.ColorTemplate;
@@ -43,40 +49,50 @@ import com.mikepenz.materialdrawer.DrawerBuilder;
 import com.mikepenz.materialdrawer.model.PrimaryDrawerItem;
 import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
 
+import java.text.DateFormat;
 import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.Locale;
+import java.util.Queue;
+import java.util.concurrent.ArrayBlockingQueue;
 
 import info.nightscout.android.R;
 import info.nightscout.android.USB.UsbHidDriver;
 import info.nightscout.android.eula.Eula;
 import info.nightscout.android.eula.Eula.OnEulaAgreedTo;
+import info.nightscout.android.medtronic.service.MedtronicCnlAlarmReceiver;
 import info.nightscout.android.medtronic.service.MedtronicCnlIntentService;
-import info.nightscout.android.model.CgmStatusEvent;
 import info.nightscout.android.model.medtronicNg.ContourNextLinkInfo;
+import info.nightscout.android.model.medtronicNg.PumpInfo;
+import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
 import info.nightscout.android.settings.SettingsActivity;
-import info.nightscout.android.upload.MedtronicNG.PumpStatusRecord;
 import info.nightscout.android.upload.nightscout.NightscoutUploadIntentService;
 import io.realm.Realm;
 import io.realm.RealmResults;
 import io.realm.Sort;
 import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper;
 
-/* Main activity for the MainActivity program */
 public class MainActivity extends AppCompatActivity implements OnSharedPreferenceChangeListener, OnEulaAgreedTo {
     private static final String TAG = MainActivity.class.getSimpleName();
 
     public static int batLevel = 0;
-    public static PumpStatusRecord pumpStatusRecord = new PumpStatusRecord();
+    private static long activePumpMac;
     boolean mEnableCgmService = true;
     SharedPreferences prefs = null;
+    private PumpInfo mActivePump;
     private TextView mTextViewLog; // This will eventually move to a status page.
-    private Intent mCnlIntentService;
+    private LineChart mChart;
     private Intent mNightscoutUploadService;
     private Handler mUiRefreshHandler = new Handler();
     private Runnable mUiRefreshRunnable = new RefreshDisplayRunnable();
     private Realm mRealm;
+    private StatusMessageReceiver statusMessageReceiver = new StatusMessageReceiver();
+    private MedtronicCnlAlarmReceiver medtronicCnlAlarmReceiver = new MedtronicCnlAlarmReceiver();
+
+    public static void setActivePumpMac(long pumpMac) {
+        activePumpMac = pumpMac;
+    }
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -84,7 +100,6 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         super.onCreate(savedInstanceState);
 
         mRealm = Realm.getDefaultInstance();
-        mCnlIntentService = new Intent(this, MedtronicCnlIntentService.class);
         mNightscoutUploadService = new Intent(this, NightscoutUploadIntentService.class);
 
         setContentView(R.layout.activity_main);
@@ -96,8 +111,30 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
             stopCgmService();
         }
 
+        // Disable battery optimization to avoid missing values on 6.0+
+        // taken from https://github.com/NightscoutFoundation/xDrip/blob/master/app/src/main/java/com/eveningoutpost/dexdrip/Home.java#L277L298
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            final String packageName = getPackageName();
+            //Log.d(TAG, "Maybe ignoring battery optimization");
+            final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+            if (!pm.isIgnoringBatteryOptimizations(packageName)) {
+                Log.d(TAG, "Requesting ignore battery optimization");
+                try {
+                    // ignoring battery optimizations required for constant connection
+                    // to peripheral device - eg CGM transmitter.
+                    final Intent intent = new Intent();
+                    intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+                    intent.setData(Uri.parse("package:" + packageName));
+                    startActivity(intent);
+                } catch (ActivityNotFoundException e) {
+                    Log.d(TAG, "Device does not appear to support battery optimization whitelisting!");
+                }
+            }
+        }
+
         LocalBroadcastManager.getInstance(this).registerReceiver(
-                new StatusMessageReceiver(),
+                statusMessageReceiver,
                 new IntentFilter(MedtronicCnlIntentService.Constants.ACTION_STATUS_MESSAGE));
         LocalBroadcastManager.getInstance(this).registerReceiver(
                 new RefreshDataReceiver(),
@@ -138,7 +175,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                 .withIcon(GoogleMaterial.Icon.gmd_settings)
                 .withSelectable(false);
         final PrimaryDrawerItem itemRegisterUsb = new PrimaryDrawerItem()
-                .withName("Register Contour Next Link")
+                .withName("Registered Devices")
                 .withIcon(GoogleMaterial.Icon.gmd_usb)
                 .withSelectable(false);
         final PrimaryDrawerItem itemStopCollecting = new PrimaryDrawerItem()
@@ -186,7 +223,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                         } else if (drawerItem.equals(itemGetNow)) {
                             startCgmService();
                         } else if (drawerItem.equals(itemClearLog)) {
-                            mTextViewLog.setText("", BufferType.EDITABLE);
+                            clearLogText();
                         }
 
                         return false;
@@ -195,6 +232,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                 .build();
 
         mTextViewLog = (TextView) findViewById(R.id.textview_log);
+        mChart = (LineChart) findViewById(R.id.chart);
     }
 
     @Override
@@ -207,6 +245,9 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
     @Override
     protected void attachBaseContext(Context newBase) {
         super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
+
+        // setup self handling alarm receiver
+        medtronicCnlAlarmReceiver.setContext(getBaseContext());
     }
 
     @Override
@@ -231,8 +272,8 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
     private boolean hasDetectedCnl() {
         if (mRealm.where(ContourNextLinkInfo.class).count() == 0) {
             new AlertDialog.Builder(this, R.style.AppTheme)
-                    .setTitle("Contour Next Link not detected")
-                    .setMessage("To register a Contour Next Link you must first plug it in.")
+                    .setTitle("No registered Contour Next Link devices")
+                    .setMessage("To register a Contour Next Link you must first plug it in, and get a reading from the pump.")
                     .setCancelable(false)
                     .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                         public void onClick(DialogInterface dialog, int which) {
@@ -251,7 +292,6 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         UsbDevice cnlDevice = UsbHidDriver.getUsbDevice(usbManager, MedtronicCnlIntentService.USB_VID, MedtronicCnlIntentService.USB_PID);
 
         return !(usbManager != null && cnlDevice != null && !usbManager.hasPermission(cnlDevice));
-
     }
 
     private void waitForUsbPermission() {
@@ -276,6 +316,11 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         startDisplayRefreshLoop();
     }
 
+    private void clearLogText() {
+        statusMessageReceiver.clearMessages();
+        //mTextViewLog.setText("", BufferType.EDITABLE);
+    }
+
     private void startDisplayRefreshLoop() {
         mUiRefreshHandler.post(mUiRefreshRunnable);
     }
@@ -285,41 +330,21 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
     }
 
     private void startCgmService() {
-        startCgmService(0L);
+        startCgmService(System.currentTimeMillis() + 1000);
     }
 
-    private void startCgmService(long runAtTime) {
+    private void startCgmService(long initialPoll) {
         Log.i(TAG, "startCgmService called");
 
         if (!mEnableCgmService) {
             return;
         }
 
-        if (runAtTime == 0L) {
-            startService(mCnlIntentService);
-        } else {
-            AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
-            PendingIntent pending = PendingIntent.getService(this, 0, mCnlIntentService, 0);
-
-            alarmManager.set(AlarmManager.RTC_WAKEUP, runAtTime, pending);
-        }
-    }
-
-    private void startCgmServicePolling(long initialPoll) {
-        Log.i(TAG, "startCgmServicePolling called");
-
-        if (!mEnableCgmService) {
-            return;
-        }
+        //clearLogText();
 
         // Cancel any existing polling.
         stopCgmService();
-
-        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
-        PendingIntent pending = PendingIntent.getService(this, 0, mCnlIntentService, 0);
-
-        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
-                initialPoll, MedtronicCnlIntentService.POLL_PERIOD_MS, pending);
+        medtronicCnlAlarmReceiver.setAlarm(initialPoll);
     }
 
     private void uploadCgmData() {
@@ -328,11 +353,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
 
     private void stopCgmService() {
         Log.i(TAG, "stopCgmService called");
-
-        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
-        PendingIntent pending = PendingIntent.getService(this, 0, mCnlIntentService, 0);
-
-        alarmManager.cancel(pending);
+        medtronicCnlAlarmReceiver.cancelAlarm();
     }
 
     private void showDisconnectionNotification(String title, String message) {
@@ -369,15 +390,16 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
     @Override
     protected void onDestroy() {
         Log.i(TAG, "onDestroy called");
+        super.onDestroy();
+
         PreferenceManager.getDefaultSharedPreferences(getBaseContext()).unregisterOnSharedPreferenceChangeListener(this);
         cancelDisplayRefreshLoop();
 
+        mRealm.close();
+
         if (!mEnableCgmService) {
             stopCgmService();
         }
-
-        mRealm.close();
-        super.onDestroy();
     }
 
     @Override
@@ -418,7 +440,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         }
     }
 
-    private String renderTrendHtml(CgmStatusEvent.TREND trend) {
+    private String renderTrendHtml(PumpStatusEvent.CGM_TREND trend) {
         switch (trend) {
             case DOUBLE_UP:
                 return "&#x21c8;";
@@ -439,13 +461,88 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         }
     }
 
+    private PumpInfo getActivePump() {
+        if (activePumpMac != 0L && (mActivePump == null || !mActivePump.isValid() || mActivePump.getPumpMac() != activePumpMac)) {
+            mActivePump = null;
+
+            PumpInfo pump = mRealm
+                    .where(PumpInfo.class)
+                    .equalTo("pumpMac", MainActivity.activePumpMac)
+                    .findFirst();
+
+            if (pump != null && pump.isValid()) {
+                mActivePump = pump;
+            }
+        }
+
+        return mActivePump;
+    }
+
     private class StatusMessageReceiver extends BroadcastReceiver {
+        private class StatusMessage {
+            private long timestamp;
+            private String message;
+
+            public StatusMessage(String message) {
+                this(System.currentTimeMillis(), message);
+            }
+
+            public StatusMessage(long timestamp, String message) {
+                this.timestamp = timestamp;
+                this.message = message;
+            }
+
+            public long getTimestamp() {
+                return timestamp;
+            }
+
+            public void setTimestamp(long timestamp) {
+                this.timestamp = timestamp;
+            }
+
+            public String getMessage() {
+                return message;
+            }
+
+            public void setMessage(String message) {
+                this.message = message;
+            }
+
+            public String toString() {
+                return DateFormat.getTimeInstance(DateFormat.MEDIUM).format(timestamp) + ": " + message;
+            }
+        }
+
+        private Queue<StatusMessage> messages = new ArrayBlockingQueue<>(10);
+
         @Override
         public void onReceive(Context context, Intent intent) {
             String message = intent.getStringExtra(MedtronicCnlIntentService.Constants.EXTENDED_DATA);
-
             Log.i(TAG, "Message Receiver: " + message);
-            mTextViewLog.setText(mTextViewLog.getText() + "\n" + message, BufferType.EDITABLE);
+
+            synchronized (messages) {
+                while (messages.size() > 8) {
+                    messages.poll();
+                }
+                messages.add(new StatusMessage(message));
+            }
+
+            StringBuilder sb = new StringBuilder();
+            for (StatusMessage msg : messages) {
+                if (sb.length() > 0)
+                    sb.append("\n");
+                sb.append(msg);
+            }
+
+            mTextViewLog.setText(sb.toString(), BufferType.EDITABLE);
+        }
+
+        public void clearMessages() {
+            synchronized (messages) {
+                messages.clear();
+            }
+
+            mTextViewLog.setText("", BufferType.EDITABLE);
         }
     }
 
@@ -463,20 +560,22 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
             }
             TextView textViewTrend = (TextView) findViewById(R.id.textview_trend);
             TextView textViewIOB = (TextView) findViewById(R.id.textview_iob);
-            //LineChart chart = (LineChart) findViewById(R.id.chart);
 
-            // Get the most recently written CGM record.
-            RealmResults<CgmStatusEvent> results =
-                    mRealm.where(CgmStatusEvent.class)
-                            .findAllSorted("eventDate", Sort.ASCENDING);
+            // Get the most recently written CGM record for the active pump.
+            PumpStatusEvent pumpStatusData = null;
 
-            CgmStatusEvent cgmRecord = null;
+            PumpInfo pump = getActivePump();
 
-            if (results.size() > 0) {
-                cgmRecord = results.last();
+            if (pump != null && pump.isValid()) {
+                pumpStatusData = pump.getPumpHistory().last();
             }
 
-            if (cgmRecord == null) {
+            // FIXME - grab the last item from the activePump's getPumpHistory
+            RealmResults<PumpStatusEvent> results =
+                    mRealm.where(PumpStatusEvent.class)
+                            .findAllSorted("eventDate", Sort.ASCENDING);
+
+            if (pumpStatusData == null) {
                 return;
             }
 
@@ -488,34 +587,34 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
 
             String sgvString, units;
             if (prefs.getBoolean("mmolxl", false)) {
-                float fBgValue = (float) cgmRecord.getSgv();
+                float fBgValue = (float) pumpStatusData.getSgv();
                 sgvString = df.format(fBgValue / 18.016f);
                 units = "mmol/L";
                 Log.d(TAG, "mmolxl true --> " + sgvString);
 
             } else {
-                sgvString = String.valueOf(cgmRecord.getSgv());
+                sgvString = String.valueOf(pumpStatusData.getSgv());
                 units = "mg/dL";
                 Log.d(TAG, "mmolxl false --> " + sgvString);
             }
 
             textViewBg.setText(sgvString);
             textViewUnits.setText(units);
-            textViewBgTime.setText(DateUtils.getRelativeTimeSpanString(cgmRecord.getEventDate().getTime()));
-            textViewTrend.setText(Html.fromHtml(renderTrendHtml(cgmRecord.getTrend())));
-            textViewIOB.setText(String.format(Locale.getDefault(), "%.2f", pumpStatusRecord.activeInsulin));
+            textViewBgTime.setText(DateUtils.getRelativeTimeSpanString(pumpStatusData.getEventDate().getTime()));
+            textViewTrend.setText(Html.fromHtml(renderTrendHtml(pumpStatusData.getCgmTrend())));
+            textViewIOB.setText(String.format(Locale.getDefault(), "%.2f", pumpStatusData.getActiveInsulin()));
 
             // TODO - waiting for MPAndroidCharts 3.0.0. This will fix:
             // Date support
             // Realm v1.0.0 support
-            // updateChart(results);
+            //updateChart(results);
 
             // Run myself again in 60 seconds;
             mUiRefreshHandler.postDelayed(this, 60000L);
         }
 
-        private void updateChart(RealmResults<CgmStatusEvent> results) {
-            RealmLineDataSet<CgmStatusEvent> lineDataSet = new RealmLineDataSet<>(results, "sgv", "eventDate");
+        private void updateChart(RealmResults<PumpStatusEvent> results) {
+            RealmLineDataSet<PumpStatusEvent> lineDataSet = new RealmLineDataSet<>(results, "eventDate", "sgv");
 
             lineDataSet.setDrawCircleHole(false);
             lineDataSet.setColor(ColorTemplate.rgb("#FF5722"));
@@ -526,11 +625,11 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
             ArrayList<ILineDataSet> dataSets = new ArrayList<ILineDataSet>();
             dataSets.add(lineDataSet);
 
-            RealmLineData lineData = new RealmLineData(results, "eventDate", dataSets);
+            LineData lineData = new LineData(dataSets);
 
             // set data
-            //chart.setMinimumHeight(200);
-            //chart.setData(lineData);
+            mChart.setMinimumHeight(200);
+            mChart.setData(lineData);
         }
     }
 
@@ -538,18 +637,30 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
 
         @Override
         public void onReceive(Context context, Intent intent) {
-            CgmStatusEvent record = mRealm.where(CgmStatusEvent.class)
-                    .findAll()
-                    .last();
+            // If the MainActivity has already been destroyed (meaning the Realm instance has been closed)
+            // then don't worry about processing this broadcast
+            if (mRealm.isClosed()) {
+                return;
+            }
+
+            PumpStatusEvent pumpStatusData = null;
 
-            long nextPoll = record.getEventDate().getTime() + MedtronicCnlIntentService.POLL_GRACE_PERIOD_MS + MedtronicCnlIntentService.POLL_PERIOD_MS;
-            startCgmServicePolling(nextPoll);
-            Log.d(TAG, "Next Poll at " + new Date(nextPoll).toString());
+            PumpInfo pump = getActivePump();
+
+            if (pump != null && pump.isValid()) {
+                pumpStatusData = pump.getPumpHistory().last();
+            } else {
+                return;
+            }
+
+            long nextPoll = pumpStatusData.getEventDate().getTime() + pumpStatusData.getPumpTimeOffset()
+                    + MedtronicCnlIntentService.POLL_GRACE_PERIOD_MS + MedtronicCnlIntentService.POLL_PERIOD_MS;
+            startCgmService(nextPoll);
 
             // Delete invalid or old records from Realm
             // TODO - show an error message if the valid records haven't been uploaded
-            final RealmResults<CgmStatusEvent> results =
-                    mRealm.where(CgmStatusEvent.class)
+            final RealmResults<PumpStatusEvent> results =
+                    mRealm.where(PumpStatusEvent.class)
                             .equalTo("sgv", 0)
                             .or()
                             .lessThan("eventDate", new Date(System.currentTimeMillis() - (24 * 60 * 60 * 1000)))
@@ -569,6 +680,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
             // TODO - handle isOffline in NightscoutUploadIntentService?
             uploadCgmData();
 
+
             refreshDisplay();
         }
     }
diff --git a/app/src/main/java/info/nightscout/android/medtronic/MedtronicCNLSession.java b/app/src/main/java/info/nightscout/android/medtronic/MedtronicCNLSession.java
deleted file mode 100644
index 6c1ac2f55a6295cb70d7ada74b9825bd6755926f..0000000000000000000000000000000000000000
--- a/app/src/main/java/info/nightscout/android/medtronic/MedtronicCNLSession.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package info.nightscout.android.medtronic;
-
-/**
- * Created by lgoedhart on 26/03/2016.
- */
-public class MedtronicCNLSession {
-    private byte[] HMAC;
-    private byte[] key;
-
-    private long linkMAC;
-    private long pumpMAC;
-
-    private byte radioChannel;
-    private int bayerSequenceNumber = 1;
-    private int medtronicSequenceNumber = 1;
-
-    public byte[] getHMAC() {
-        return HMAC;
-    }
-
-    public byte[] getKey() {
-        return key;
-    }
-    public byte[] getIV() {
-        byte[] iv = new byte[key.length];
-        System.arraycopy(key,0,iv,0,key.length);
-        iv[0] = radioChannel;
-        return iv;
-    }
-
-    public long getLinkMAC() {
-        return linkMAC;
-    }
-
-    public void setLinkMAC( long linkMAC ) {
-        this.linkMAC = linkMAC;
-    }
-
-    public long getPumpMAC() {
-        return pumpMAC;
-    }
-
-    public void setPumpMAC( long pumpMAC ) {
-        this.pumpMAC = pumpMAC;
-    }
-
-    public int getBayerSequenceNumber() {
-        return bayerSequenceNumber;
-    }
-
-    public int getMedtronicSequenceNumber() {
-        return medtronicSequenceNumber;
-    }
-
-    public byte getRadioChannel() {
-        return radioChannel;
-    }
-
-    public void incrBayerSequenceNumber() {
-        bayerSequenceNumber++;
-    }
-
-    public void incrMedtronicSequenceNumber() {
-        medtronicSequenceNumber++;
-    }
-
-    public void setRadioChannel(byte radioChannel) {
-        this.radioChannel = radioChannel;
-    }
-
-    public void setHMAC( byte[] hmac ) {
-        this.HMAC = hmac;
-    }
-
-    public void setKey( byte[] key ) {
-        this.key = key;
-    }
-}
diff --git a/app/src/main/java/info/nightscout/android/medtronic/MedtronicCNLReader.java b/app/src/main/java/info/nightscout/android/medtronic/MedtronicCnlReader.java
similarity index 57%
rename from app/src/main/java/info/nightscout/android/medtronic/MedtronicCNLReader.java
rename to app/src/main/java/info/nightscout/android/medtronic/MedtronicCnlReader.java
index 8b56ea37e359278c03f25173894a2bd12f9b5ea0..6dd908f0dd3f57b8d8c585d51690eaee4b82df5b 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/MedtronicCNLReader.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/MedtronicCnlReader.java
@@ -2,11 +2,16 @@ package info.nightscout.android.medtronic;
 
 import android.util.Log;
 
+import org.apache.commons.lang3.ArrayUtils;
+
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.Locale;
 import java.util.concurrent.TimeoutException;
@@ -25,22 +30,24 @@ import info.nightscout.android.medtronic.message.EncryptionException;
 import info.nightscout.android.medtronic.message.EndEHSMMessage;
 import info.nightscout.android.medtronic.message.MedtronicMessage;
 import info.nightscout.android.medtronic.message.MessageUtils;
+import info.nightscout.android.medtronic.message.PumpBasalPatternRequestMessage;
+import info.nightscout.android.medtronic.message.PumpBasalPatternResponseMessage;
 import info.nightscout.android.medtronic.message.PumpStatusRequestMessage;
 import info.nightscout.android.medtronic.message.PumpStatusResponseMessage;
 import info.nightscout.android.medtronic.message.PumpTimeRequestMessage;
 import info.nightscout.android.medtronic.message.PumpTimeResponseMessage;
 import info.nightscout.android.medtronic.message.ReadInfoResponseMessage;
+import info.nightscout.android.medtronic.message.RequestLinkKeyResponseMessage;
 import info.nightscout.android.medtronic.message.UnexpectedMessageException;
-import info.nightscout.android.medtronic.service.MedtronicCnlIntentService;
-import info.nightscout.android.model.CgmStatusEvent;
+import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
 import info.nightscout.android.utils.HexDump;
 
 /**
  * Created by lgoedhart on 24/03/2016.
  */
-public class MedtronicCNLReader implements ContourNextLinkMessageHandler {
+public class MedtronicCnlReader implements ContourNextLinkMessageHandler {
 
-    private static final String TAG = MedtronicCnlIntentService.class.getSimpleName();
+    private static final String TAG = MedtronicCnlReader.class.getSimpleName();
 
     private static final int USB_BLOCKSIZE = 64;
     private static final int READ_TIMEOUT_MS = 5000;
@@ -49,19 +56,40 @@ public class MedtronicCNLReader implements ContourNextLinkMessageHandler {
     private static final byte[] RADIO_CHANNELS = {0x14, 0x11, 0x0e, 0x17, 0x1a};
     private UsbHidDriver mDevice;
 
-    private MedtronicCNLSession mPumpSession = new MedtronicCNLSession();
+    private MedtronicCnlSession mPumpSession = new MedtronicCnlSession();
 
     private String mStickSerial = null;
 
-    public MedtronicCNLReader(UsbHidDriver device) {
+    public MedtronicCnlReader(UsbHidDriver device) {
         mDevice = device;
     }
 
+    private static PumpStatusEvent.CGM_TREND fromMessageByte(byte messageByte) {
+        switch (messageByte) {
+            case (byte) 0x60:
+                return PumpStatusEvent.CGM_TREND.FLAT;
+            case (byte) 0xc0:
+                return PumpStatusEvent.CGM_TREND.DOUBLE_UP;
+            case (byte) 0xa0:
+                return PumpStatusEvent.CGM_TREND.SINGLE_UP;
+            case (byte) 0x80:
+                return PumpStatusEvent.CGM_TREND.FOURTY_FIVE_UP;
+            case (byte) 0x40:
+                return PumpStatusEvent.CGM_TREND.FOURTY_FIVE_DOWN;
+            case (byte) 0x20:
+                return PumpStatusEvent.CGM_TREND.SINGLE_DOWN;
+            case (byte) 0x00:
+                return PumpStatusEvent.CGM_TREND.DOUBLE_DOWN;
+            default:
+                return PumpStatusEvent.CGM_TREND.NOT_COMPUTABLE;
+        }
+    }
+
     public String getStickSerial() {
         return mStickSerial;
     }
 
-    public MedtronicCNLSession getPumpSession() {
+    public MedtronicCnlSession getPumpSession() {
         return mPumpSession;
     }
 
@@ -70,28 +98,28 @@ public class MedtronicCNLReader implements ContourNextLinkMessageHandler {
 
         byte[] responseBuffer = new byte[USB_BLOCKSIZE];
         int bytesRead;
-        int messageSize;
+        int messageSize = 0;
 
         do {
             bytesRead = mDevice.read(responseBuffer, READ_TIMEOUT_MS);
 
-            if (bytesRead == 0) {
+            if (bytesRead == -1) {
                 throw new TimeoutException("Timeout waiting for response from pump");
+            } else if (bytesRead > 0) {
+                // Validate the header
+                ByteBuffer header = ByteBuffer.allocate(3);
+                header.put(responseBuffer, 0, 3);
+                String headerString = new String(header.array());
+                if (!headerString.equals(BAYER_USB_HEADER)) {
+                    throw new IOException("Unexpected header received");
+                }
+                messageSize = responseBuffer[3];
+                responseMessage.write(responseBuffer, 4, messageSize);
+            } else {
+                Log.w(TAG, "readMessage: got a zero-sized response.");
             }
+        } while (bytesRead > 0 && messageSize == 60);
 
-            // Validate the header
-            ByteBuffer header = ByteBuffer.allocate(3);
-            header.put(responseBuffer, 0, 3);
-            String headerString = new String(header.array());
-            if (!headerString.equals(BAYER_USB_HEADER)) {
-                throw new IOException("Unexpected header received");
-            }
-            messageSize = responseBuffer[3];
-            responseMessage.write(responseBuffer, 4, messageSize);
-        } while (bytesRead > 0 && (messageSize + 4) == bytesRead);
-        // TODO - how to deal with messages that finish on the boundary?
-
-        // FIXME - remove debugging
         String responseString = HexDump.dumpHexString(responseMessage.toByteArray());
         Log.d(TAG, "READ: " + responseString);
 
@@ -182,27 +210,6 @@ public class MedtronicCNLReader implements ContourNextLinkMessageHandler {
         }
     }
 
-    private static CgmStatusEvent.TREND fromMessageByte(byte messageByte) {
-        switch( messageByte ) {
-            case (byte) 0x60:
-                return CgmStatusEvent.TREND.FLAT;
-            case (byte) 0xc0:
-                return CgmStatusEvent.TREND.DOUBLE_UP;
-            case (byte) 0xa0:
-                return CgmStatusEvent.TREND.SINGLE_UP;
-            case (byte) 0x80:
-                return CgmStatusEvent.TREND.FOURTY_FIVE_UP;
-            case (byte) 0x40:
-                return CgmStatusEvent.TREND.FOURTY_FIVE_DOWN;
-            case (byte) 0x20:
-                return CgmStatusEvent.TREND.SINGLE_DOWN;
-            case (byte) 0x00:
-                return CgmStatusEvent.TREND.DOUBLE_DOWN;
-            default:
-                return CgmStatusEvent.TREND.NOT_COMPUTABLE;
-        }
-    }
-
     public void enterControlMode() throws IOException, TimeoutException, UnexpectedMessageException {
         boolean doRetry = false;
 
@@ -223,21 +230,26 @@ public class MedtronicCNLReader implements ContourNextLinkMessageHandler {
     }
 
     public void enterPassthroughMode() throws IOException, TimeoutException, UnexpectedMessageException {
+        Log.d(TAG, "Begin enterPasshtroughMode");
         new ContourNextLinkCommandMessage("W|").send(this);
         checkControlMessage(readMessage(), ASCII.ACK.value);
         new ContourNextLinkCommandMessage("Q|").send(this);
         checkControlMessage(readMessage(), ASCII.ACK.value);
         new ContourNextLinkCommandMessage("1|").send(this);
         checkControlMessage(readMessage(), ASCII.ACK.value);
+        Log.d(TAG, "Finished enterPasshtroughMode");
     }
 
-    public void openConnection() throws IOException, TimeoutException {
+    public void openConnection() throws IOException, TimeoutException, NoSuchAlgorithmException {
+        Log.d(TAG, "Begin openConnection");
         new ContourNextLinkBinaryMessage(ContourNextLinkBinaryMessage.CommandType.OPEN_CONNECTION, mPumpSession, mPumpSession.getHMAC()).send(this);
         // FIXME - We need to care what the response message is - wrong MAC and all that
         readMessage();
+        Log.d(TAG, "Finished openConnection");
     }
 
     public void requestReadInfo() throws IOException, TimeoutException, EncryptionException, ChecksumException {
+        Log.d(TAG, "Begin requestReadInfo");
         new ContourNextLinkBinaryMessage(ContourNextLinkBinaryMessage.CommandType.READ_INFO, mPumpSession, null).send(this);
 
         ContourNextLinkMessage response = ReadInfoResponseMessage.fromBytes(mPumpSession, readMessage());
@@ -251,19 +263,54 @@ public class MedtronicCNLReader implements ContourNextLinkMessageHandler {
 
         this.getPumpSession().setLinkMAC(linkMAC);
         this.getPumpSession().setPumpMAC(pumpMAC);
+        Log.d(TAG, String.format("Finished requestReadInfo. linkMAC = '%d', pumpMAC = '%d", linkMAC, pumpMAC));
+    }
+
+    public void requestLinkKey() throws IOException, TimeoutException, EncryptionException, ChecksumException {
+        Log.d(TAG, "Begin requestLinkKey");
+        new ContourNextLinkBinaryMessage(ContourNextLinkBinaryMessage.CommandType.REQUEST_LINK_KEY, mPumpSession, null).send(this);
+
+        ContourNextLinkMessage response = RequestLinkKeyResponseMessage.fromBytes(mPumpSession, readMessage());
+
+        // FIXME - this needs to go into RequestLinkKeyResponseMessage
+        ByteBuffer infoBuffer = ByteBuffer.allocate(55);
+        infoBuffer.order(ByteOrder.BIG_ENDIAN);
+        infoBuffer.put(response.encode(), 0x21, 55);
+
+        byte[] packedLinkKey = infoBuffer.array();
+
+        this.getPumpSession().setPackedLinkKey(packedLinkKey);
+
+        Log.d(TAG, String.format("Finished requestLinkKey. linkKey = '%s'", this.getPumpSession().getKey()));
     }
 
-    public byte negotiateChannel() throws IOException, ChecksumException, TimeoutException {
-        for (byte channel : RADIO_CHANNELS) {
+    public byte negotiateChannel(byte lastRadioChannel) throws IOException, ChecksumException, TimeoutException {
+        ArrayList<Byte> radioChannels = new ArrayList<>(Arrays.asList(ArrayUtils.toObject(RADIO_CHANNELS)));
+
+        if (lastRadioChannel != 0x00) {
+            // If we know the last channel that was used, shuffle the negotiation order
+            Byte lastChannel = radioChannels.remove(radioChannels.indexOf(lastRadioChannel));
+
+            if (lastChannel != null) {
+                radioChannels.add(0, lastChannel);
+            }
+        }
+
+        Log.d(TAG, "Begin negotiateChannel");
+        for (byte channel : radioChannels) {
+            Log.d(TAG, String.format("negotiateChannel: trying channel '%d'...", channel));
             mPumpSession.setRadioChannel(channel);
             new ChannelNegotiateMessage(mPumpSession).send(this);
 
             // Don't care what the 0x81 response message is at this stage
+            Log.d(TAG, "negotiateChannel: Reading 0x81 message");
             readMessage();
             // The 0x80 message
+            Log.d(TAG, "negotiateChannel: Reading 0x80 message");
             ContourNextLinkMessage response = ContourNextLinkBinaryMessage.fromBytes(readMessage());
             byte[] responseBytes = response.encode();
 
+            Log.d(TAG, "negotiateChannel: Check response length");
             if (responseBytes.length > 46) {
                 // Looks promising, let's check the last byte of the payload to make sure
                 if (responseBytes[76] == mPumpSession.getRadioChannel()) {
@@ -276,18 +323,21 @@ public class MedtronicCNLReader implements ContourNextLinkMessageHandler {
             }
         }
 
+        Log.d(TAG, String.format("Finished negotiateChannel with channel '%d'", mPumpSession.getRadioChannel()));
         return mPumpSession.getRadioChannel();
     }
 
     public void beginEHSMSession() throws EncryptionException, IOException, TimeoutException {
+        Log.d(TAG, "Begin beginEHSMSession");
         new BeginEHSMMessage(mPumpSession).send(this);
         // The Begin EHSM Session only has an 0x81 response
         readMessage();
+        Log.d(TAG, "Finished beginEHSMSession");
     }
 
     public Date getPumpTime() throws EncryptionException, IOException, ChecksumException, TimeoutException {
+        Log.d(TAG, "Begin getPumpTime");
         // FIXME - throw if not in EHSM mode (add a state machine)
-        Date timeAtCapture = new Date();
 
         new PumpTimeRequestMessage(mPumpSession).send(this);
         // Read the 0x81
@@ -296,8 +346,10 @@ public class MedtronicCNLReader implements ContourNextLinkMessageHandler {
         // Read the 0x80
         ContourNextLinkMessage response = PumpTimeResponseMessage.fromBytes(mPumpSession, readMessage());
 
-        if (response.encode().length < 57) {
+        if (response.encode().length < (61 + 8)) {
             // Invalid message. Return an invalid date.
+            // TODO - deal with this more elegantly
+            Log.e(TAG, "Invalid message received for getPumpTime");
             return new Date();
         }
 
@@ -308,10 +360,12 @@ public class MedtronicCNLReader implements ContourNextLinkMessageHandler {
         long rtc = dateBuffer.getInt(0) & 0x00000000ffffffffL;
         long offset = dateBuffer.getInt(4);
 
+        Log.d(TAG, "Finished getPumpTime with date " + MessageUtils.decodeDateTime(rtc, offset));
         return MessageUtils.decodeDateTime(rtc, offset);
     }
 
-    public void getPumpStatus(CgmStatusEvent cgmRecord, long pumpTimeOffset) throws IOException, EncryptionException, ChecksumException, TimeoutException {
+    public void getPumpStatus(PumpStatusEvent pumpRecord, long pumpTimeOffset) throws IOException, EncryptionException, ChecksumException, TimeoutException {
+        Log.d(TAG, "Begin getPumpStatus");
         // FIXME - throw if not in EHSM mode (add a state machine)
 
         new PumpStatusRequestMessage(mPumpSession).send(this);
@@ -321,8 +375,10 @@ public class MedtronicCNLReader implements ContourNextLinkMessageHandler {
         // Read the 0x80
         ContourNextLinkMessage response = PumpStatusResponseMessage.fromBytes(mPumpSession, readMessage());
 
-        if (response.encode().length < 57) {
+        if (response.encode().length < (57 + 96)) {
             // Invalid message. Don't try and parse it
+            // TODO - deal with this more elegantly
+            Log.e(TAG, "Invalid message received for getPumpStatus");
             return;
         }
 
@@ -331,55 +387,143 @@ public class MedtronicCNLReader implements ContourNextLinkMessageHandler {
         statusBuffer.order(ByteOrder.BIG_ENDIAN);
         statusBuffer.put(response.encode(), 0x39, 96);
 
-        // Read the data into the record
+        // Status Flags
+        pumpRecord.setSuspended((statusBuffer.get(0x03) & 0x01) != 0x00);
+        pumpRecord.setBolusing((statusBuffer.get(0x03) & 0x02) != 0x00);
+        pumpRecord.setDeliveringInsulin((statusBuffer.get(0x03) & 0x10) != 0x00);
+        pumpRecord.setTempBasalActive((statusBuffer.get(0x03) & 0x20) != 0x00);
+        pumpRecord.setCgmActive((statusBuffer.get(0x03) & 0x40) != 0x00);
+
+        // Active basal pattern
+        pumpRecord.setActiveBasalPattern(statusBuffer.get(0x1a));
+
+        // Normal basal rate
+        long rawNormalBasal = statusBuffer.getInt(0x1b);
+        pumpRecord.setBasalRate(new BigDecimal(rawNormalBasal / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue());
+
+        // Temp basal rate
+        // TODO - need to figure this one out
+        //long rawTempBasal = statusBuffer.getShort(0x21) & 0x0000ffff;
+        //pumpRecord.setTempBasalRate(new BigDecimal(rawTempBasal / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue());
+
+        // Temp basal percentage
+        pumpRecord.setTempBasalPercentage(statusBuffer.get(0x23));
+
+        // Temp basal minutes remaining
+        pumpRecord.setTempBasalMinutesRemaining((short) (statusBuffer.getShort(0x24) & 0x0000ffff));
+
+        // Units of insulin delivered as basal today
+        // TODO - is this basal? Do we have a total Units delivered elsewhere?
+        pumpRecord.setBasalUnitsDeliveredToday(statusBuffer.getInt(0x26));
+
+        // Pump battery percentage
+        pumpRecord.setBatteryPercentage((statusBuffer.get(0x2a)));
+
+        // Reservoir amount
+        long rawReservoirAmount = statusBuffer.getInt(0x2b);
+        pumpRecord.setReservoirAmount(new BigDecimal(rawReservoirAmount / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue());
+
+        // Amount of insulin left in pump (in minutes)
+        byte insulinHours = statusBuffer.get(0x2f);
+        byte insulinMinutes = statusBuffer.get(0x30);
+        pumpRecord.setMinutesOfInsulinRemaining((short) ((insulinHours * 60) + insulinMinutes));
+
+        // Active insulin
         long rawActiveInsulin = statusBuffer.getShort(0x33) & 0x0000ffff;
-        MainActivity.pumpStatusRecord.activeInsulin = new BigDecimal(rawActiveInsulin / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP);
-        cgmRecord.setSgv(statusBuffer.getShort(0x35) & 0x0000ffff); // In mg/DL. 0 means no CGM reading
+        pumpRecord.setActiveInsulin(new BigDecimal(rawActiveInsulin / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue());
+
+        // CGM SGV
+        pumpRecord.setSgv(statusBuffer.getShort(0x35) & 0x0000ffff); // In mg/DL. 0 means no CGM reading
+
+        // SGV Date
         long rtc;
         long offset;
-        if ((cgmRecord.getSgv() & 0x200) == 0x200) {
+        if ((pumpRecord.getSgv() & 0x200) == 0x200) {
             // Sensor error. Let's reset. FIXME - solve this more elegantly later
-            cgmRecord.setSgv(0);
+            pumpRecord.setSgv(0);
             rtc = 0;
             offset = 0;
-            cgmRecord.setTrend(CgmStatusEvent.TREND.NOT_SET);
+            pumpRecord.setCgmTrend(PumpStatusEvent.CGM_TREND.NOT_SET);
         } else {
             rtc = statusBuffer.getInt(0x37) & 0x00000000ffffffffL;
             offset = statusBuffer.getInt(0x3b);
-            cgmRecord.setTrend(fromMessageByte(statusBuffer.get(0x40)));
+            pumpRecord.setCgmTrend(fromMessageByte(statusBuffer.get(0x40)));
         }
-        cgmRecord.setEventDate(new Date(MessageUtils.decodeDateTime(rtc, offset).getTime() - pumpTimeOffset));
-        MainActivity.pumpStatusRecord.recentBolusWizard = statusBuffer.get(0x48) != 0;
-        MainActivity.pumpStatusRecord.bolusWizardBGL = statusBuffer.getShort(0x49); // In mg/DL
-        long rawReservoirAmount = statusBuffer.getInt(0x2b);
-        MainActivity.pumpStatusRecord.reservoirAmount = new BigDecimal(rawReservoirAmount / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP);
-        MainActivity.pumpStatusRecord.batteryPercentage = (statusBuffer.get(0x2a));
+        // TODO - this should go in the sgvDate, and eventDate should be the time of this poll.
+        pumpRecord.setEventDate(new Date(MessageUtils.decodeDateTime(rtc, offset).getTime() - pumpTimeOffset));
+
+        // Predictive low suspend
+        // TODO - there is more status info in this byte other than just a boolean yes/no
+        pumpRecord.setLowSuspendActive(statusBuffer.get(0x3f) != 0);
+
+        // Recent Bolus Wizard BGL
+        pumpRecord.setRecentBolusWizard(statusBuffer.get(0x48) != 0);
+        pumpRecord.setBolusWizardBGL(statusBuffer.getShort(0x49) & 0x0000ffff); // In mg/DL
+
+        Log.d(TAG, "Finished getPumpStatus");
+    }
+
+    public void getBasalPatterns() throws EncryptionException, IOException, ChecksumException, TimeoutException {
+        Log.d(TAG, "Begin getBasalPatterns");
+        // FIXME - throw if not in EHSM mode (add a state machine)
+
+        new PumpBasalPatternRequestMessage(mPumpSession).send(this);
+        // Read the 0x81
+        readMessage();
+
+        // Read the 0x80
+        ContourNextLinkMessage response = PumpBasalPatternResponseMessage.fromBytes(mPumpSession, readMessage());
+
+        // TODO - determine message validity
+        /*
+        if (response.encode().length < (61 + 8)) {
+            // Invalid message.
+            // TODO - deal with this more elegantly
+            Log.e(TAG, "Invalid message received for getBasalPatterns");
+            return;
+        }
+        */
+
+        // FIXME - this needs to go into PumpBasalPatternResponseMessage
+        ByteBuffer basalRatesBuffer = ByteBuffer.allocate(96);
+        basalRatesBuffer.order(ByteOrder.BIG_ENDIAN);
+        basalRatesBuffer.put(response.encode(), 0x39, 96);
+
+        Log.d(TAG, "Finished getBasalPatterns");
     }
 
     public void endEHSMSession() throws EncryptionException, IOException, TimeoutException {
+        Log.d(TAG, "Begin endEHSMSession");
         new EndEHSMMessage(mPumpSession).send(this);
         // The End EHSM Session only has an 0x81 response
         readMessage();
+        Log.d(TAG, "Finished endEHSMSession");
     }
 
     public void closeConnection() throws IOException, TimeoutException {
+        Log.d(TAG, "Begin closeConnection");
         new ContourNextLinkBinaryMessage(ContourNextLinkBinaryMessage.CommandType.CLOSE_CONNECTION, mPumpSession, null).send(this);
         // FIXME - We need to care what the response message is - wrong MAC and all that
         readMessage();
+        Log.d(TAG, "Finished closeConnection");
     }
 
     public void endPassthroughMode() throws IOException, TimeoutException, UnexpectedMessageException {
+        Log.d(TAG, "Begin endPassthroughMode");
         new ContourNextLinkCommandMessage("W|").send(this);
         checkControlMessage(readMessage(), ASCII.ACK.value);
         new ContourNextLinkCommandMessage("Q|").send(this);
         checkControlMessage(readMessage(), ASCII.ACK.value);
         new ContourNextLinkCommandMessage("0|").send(this);
         checkControlMessage(readMessage(), ASCII.ACK.value);
+        Log.d(TAG, "Finished endPassthroughMode");
     }
 
     public void endControlMode() throws IOException, TimeoutException, UnexpectedMessageException {
+        Log.d(TAG, "Begin endControlMode");
         new ContourNextLinkCommandMessage(ASCII.EOT.value).send(this);
         checkControlMessage(readMessage(), ASCII.ENQ.value);
+        Log.d(TAG, "Finished endControlMode");
     }
 
     public enum ASCII {
diff --git a/app/src/main/java/info/nightscout/android/medtronic/MedtronicCnlSession.java b/app/src/main/java/info/nightscout/android/medtronic/MedtronicCnlSession.java
new file mode 100644
index 0000000000000000000000000000000000000000..215e7f3e6174163da35db8824c928d87f5d454bb
--- /dev/null
+++ b/app/src/main/java/info/nightscout/android/medtronic/MedtronicCnlSession.java
@@ -0,0 +1,130 @@
+package info.nightscout.android.medtronic;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Created by lgoedhart on 26/03/2016.
+ */
+public class MedtronicCnlSession {
+    private static final String HMAC_PADDING = "A4BD6CED9A42602564F413123";
+
+    private byte[] HMAC;
+    private byte[] key;
+
+    private String stickSerial;
+
+    private long linkMAC;
+    private long pumpMAC;
+
+    private byte radioChannel;
+    private int bayerSequenceNumber = 1;
+    private int medtronicSequenceNumber = 1;
+
+    /*public byte[] getHMAC() {
+        return HMAC;
+    }*/
+
+    public byte[] getHMAC() throws NoSuchAlgorithmException {
+        String shortSerial = this.stickSerial.replaceAll("\\d+-", "");
+        byte[] message = (shortSerial + HMAC_PADDING).getBytes();
+        byte[] numArray;
+
+        MessageDigest instance = MessageDigest.getInstance("SHA-256");
+        instance.update(message);
+
+        numArray = instance.digest();
+        ArrayUtils.reverse(numArray);
+
+        return numArray;
+    }
+
+    public byte[] getKey() {
+        return key;
+    }
+
+    public byte[] getIV() {
+        byte[] iv = new byte[key.length];
+        System.arraycopy(key, 0, iv, 0, key.length);
+        iv[0] = radioChannel;
+        return iv;
+    }
+
+    public long getLinkMAC() {
+        return linkMAC;
+    }
+
+    public void setLinkMAC(long linkMAC) {
+        this.linkMAC = linkMAC;
+    }
+
+    public long getPumpMAC() {
+        return pumpMAC;
+    }
+
+    public void setPumpMAC(long pumpMAC) {
+        this.pumpMAC = pumpMAC;
+    }
+
+    public int getBayerSequenceNumber() {
+        return bayerSequenceNumber;
+    }
+
+    public int getMedtronicSequenceNumber() {
+        return medtronicSequenceNumber;
+    }
+
+    public byte getRadioChannel() {
+        return radioChannel;
+    }
+
+    public void incrBayerSequenceNumber() {
+        bayerSequenceNumber++;
+    }
+
+    public void incrMedtronicSequenceNumber() {
+        medtronicSequenceNumber++;
+    }
+
+    public void setRadioChannel(byte radioChannel) {
+        this.radioChannel = radioChannel;
+    }
+
+    public void setHMAC(byte[] hmac) {
+        this.HMAC = hmac;
+    }
+
+    public void setKey(byte[] key) {
+        this.key = key;
+    }
+
+    public void setPackedLinkKey(byte[] packedLinkKey) {
+        this.key = new byte[16];
+
+        int pos = this.stickSerial.charAt(this.stickSerial.length() - 1) & 7;
+
+        for (int i = 0; i < this.key.length; i++) {
+            if ((packedLinkKey[pos + 1] & 1) == 1) {
+                this.key[i] = (byte) ~packedLinkKey[pos];
+            } else {
+                this.key[i] = packedLinkKey[pos];
+            }
+
+            if (((packedLinkKey[pos + 1] >> 1) & 1) == 0) {
+                pos += 3;
+            } else {
+                pos += 2;
+            }
+        }
+    }
+
+    public String getStickSerial() {
+        return stickSerial;
+    }
+
+    public void setStickSerial(String stickSerial) {
+        this.stickSerial = stickSerial;
+    }
+}
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/BeginEHSMMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/BeginEHSMMessage.java
index ed1c7d8f18c7cc61920428de4ec47ed5f6a1dc91..e2ee643a1a0d9f7de0a0c566c6d2d147ad6e3e19 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/BeginEHSMMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/BeginEHSMMessage.java
@@ -1,12 +1,12 @@
 package info.nightscout.android.medtronic.message;
 
-import info.nightscout.android.medtronic.MedtronicCNLSession;
+import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 /**
  * Created by lgoedhart on 26/03/2016.
  */
 public class BeginEHSMMessage extends MedtronicSendMessage {
-    public BeginEHSMMessage(MedtronicCNLSession pumpSession) throws EncryptionException {
+    public BeginEHSMMessage(MedtronicCnlSession pumpSession) throws EncryptionException {
         super(SendMessageType.BEGIN_EHSM_SESSION, pumpSession, buildPayload());
     }
 
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateMessage.java
index cce33d28ea977c9f978269c0d1938bb189e0cff2..eda3ce6f6f514f3045396216dc658b65e825e5fc 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateMessage.java
@@ -1,6 +1,6 @@
 package info.nightscout.android.medtronic.message;
 
-import info.nightscout.android.medtronic.MedtronicCNLSession;
+import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -9,11 +9,11 @@ import java.nio.ByteOrder;
  * Created by lgoedhart on 26/03/2016.
  */
 public class ChannelNegotiateMessage extends MedtronicMessage {
-    public ChannelNegotiateMessage(MedtronicCNLSession pumpSession) {
+    public ChannelNegotiateMessage(MedtronicCnlSession pumpSession) {
         super(CommandType.SEND_MESSAGE, CommandAction.CHANNEL_NEGOTIATE, pumpSession, buildPayload(pumpSession));
     }
 
-    protected static byte[] buildPayload( MedtronicCNLSession pumpSession ) {
+    protected static byte[] buildPayload( MedtronicCnlSession pumpSession ) {
         ByteBuffer payload = ByteBuffer.allocate(26);
         payload.order(ByteOrder.LITTLE_ENDIAN);
         // The MedtronicMessage sequence number is always sent as 1 for this message,
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryMessage.java
index 1f773efbdfa5e3430cce9e277c412ba3c9487765..95e94baa56ef73718e3db0d2d13e272f8b48a058 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryMessage.java
@@ -1,6 +1,6 @@
 package info.nightscout.android.medtronic.message;
 
-import info.nightscout.android.medtronic.MedtronicCNLSession;
+import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -34,13 +34,17 @@ public class ContourNextLinkBinaryMessage extends ContourNextLinkMessage{
         CommandType(int commandType) {
             value = (byte) commandType;
         }
+
+        public int getValue() {
+            return value;
+        }
     }
 
-    public ContourNextLinkBinaryMessage(CommandType commandType, MedtronicCNLSession pumpSession, byte[] payload) {
+    public ContourNextLinkBinaryMessage(CommandType commandType, MedtronicCnlSession pumpSession, byte[] payload) {
         super(buildPayload(commandType, pumpSession, payload));
     }
 
-    protected static byte[] buildPayload(CommandType commandType, MedtronicCNLSession pumpSession, byte[] payload) {
+    protected static byte[] buildPayload(CommandType commandType, MedtronicCnlSession pumpSession, byte[] payload) {
         int payloadLength = payload == null ? 0 : payload.length;
 
         ByteBuffer payloadBuffer = ByteBuffer.allocate( ENVELOPE_SIZE + payloadLength );
@@ -48,7 +52,7 @@ public class ContourNextLinkBinaryMessage extends ContourNextLinkMessage{
 
         payloadBuffer.put((byte) 0x51);
         payloadBuffer.put((byte) 0x3);
-        payloadBuffer.put("000000".getBytes()); // Text of Pump serial, but 000000 for 640g
+        payloadBuffer.put("000000".getBytes()); // Text of PumpInfo serial, but 000000 for 640g
         byte[] unknownBytes = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
         payloadBuffer.put(unknownBytes);
         payloadBuffer.put(commandType.value);
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/EndEHSMMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/EndEHSMMessage.java
index fe1adae580d34b37306f8a7ed30d536bff49448b..0364110375aec3d9c5f6b9c940f500d363758e4f 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/EndEHSMMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/EndEHSMMessage.java
@@ -1,12 +1,12 @@
 package info.nightscout.android.medtronic.message;
 
-import info.nightscout.android.medtronic.MedtronicCNLSession;
+import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 /**
  * Created by lgoedhart on 26/03/2016.
  */
 public class EndEHSMMessage extends MedtronicSendMessage {
-    public EndEHSMMessage(MedtronicCNLSession pumpSession) throws EncryptionException {
+    public EndEHSMMessage(MedtronicCnlSession pumpSession) throws EncryptionException {
         super(SendMessageType.END_EHSM_SESSION, pumpSession, buildPayload());
     }
 
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicMessage.java
index aed8d129f378cd16c779c13f83b3d0c857ccce81..25588505ab2e817307d0428108a0e2bb98ef4eb7 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicMessage.java
@@ -1,6 +1,6 @@
 package info.nightscout.android.medtronic.message;
 
-import info.nightscout.android.medtronic.MedtronicCNLSession;
+import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -29,7 +29,7 @@ public class MedtronicMessage extends ContourNextLinkBinaryMessage {
         }
     }
 
-    protected MedtronicMessage(CommandType commandType, CommandAction commandAction, MedtronicCNLSession pumpSession, byte[] payload) {
+    protected MedtronicMessage(CommandType commandType, CommandAction commandAction, MedtronicCnlSession pumpSession, byte[] payload) {
         super(commandType, pumpSession, buildPayload(commandAction, payload));
     }
 
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicReceiveMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicReceiveMessage.java
index 63d854a6e12871c1a6fae098011f5ecfb120dbb3..c2f4a7d58cf10d70925e3d2ab15e3358a4ff3a3f 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicReceiveMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicReceiveMessage.java
@@ -1,6 +1,6 @@
 package info.nightscout.android.medtronic.message;
 
-import info.nightscout.android.medtronic.MedtronicCNLSession;
+import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 import java.nio.ByteBuffer;
 
@@ -12,7 +12,7 @@ public class MedtronicReceiveMessage extends MedtronicMessage {
     static int ENCRYPTED_ENVELOPE_SIZE = 3;
     static int CRC_SIZE = 2;
 
-    protected MedtronicReceiveMessage(CommandType commandType, CommandAction commandAction, MedtronicCNLSession pumpSession, byte[] payload) {
+    protected MedtronicReceiveMessage(CommandType commandType, CommandAction commandAction, MedtronicCnlSession pumpSession, byte[] payload) {
         super(commandType, commandAction, pumpSession, payload);
     }
 
@@ -38,14 +38,17 @@ public class MedtronicReceiveMessage extends MedtronicMessage {
      * | byte receiveSequenceNumber | BE short receiveMessageType | byte[] Payload bytes | BE short CCITT CRC |
      * +----------------------------+-----------------------------+----------------------+--------------------+
      */
-    public static ContourNextLinkMessage fromBytes(MedtronicCNLSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException {
+    public static ContourNextLinkMessage fromBytes(MedtronicCnlSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException {
         // TODO - turn this into a factory
         ContourNextLinkMessage message = MedtronicMessage.fromBytes(bytes);
 
         // TODO - Validate the message, inner CCITT, serial numbers, etc
 
         // If there's not 57 bytes, then we got back a bad message. Not sure how to process these yet.
-        if( bytes.length >= 57 ) {
+        // Also, READ_INFO and REQUEST_LINK_KEY are not encrypted
+        if (bytes.length >= 57 &&
+                (bytes[18] != CommandType.READ_INFO.getValue()) &&
+                (bytes[18] != CommandType.REQUEST_LINK_KEY_RESPONSE.getValue())) {
             // Replace the encrypted bytes by their decrypted equivalent (same block size)
             byte encryptedPayloadSize = bytes[56];
 
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicSendMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicSendMessage.java
index 61d416c81b8dc87f7185d32f9a37c0690c2cf4c4..7bba4ded16da96428fd056f5b985ce821ee9e63e 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicSendMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicSendMessage.java
@@ -1,6 +1,6 @@
 package info.nightscout.android.medtronic.message;
 
-import info.nightscout.android.medtronic.MedtronicCNLSession;
+import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -18,6 +18,7 @@ public class MedtronicSendMessage extends MedtronicMessage {
         BEGIN_EHSM_SESSION(0x412),
         TIME_REQUEST(0x0403),
         READ_PUMP_STATUS_REQUEST(0x0112),
+        READ_BASAL_PATTERN_REQUEST(0x0112),
         END_EHSM_SESSION(0x412);
 
         private short value;
@@ -27,7 +28,7 @@ public class MedtronicSendMessage extends MedtronicMessage {
         }
     }
 
-    protected MedtronicSendMessage(SendMessageType sendMessageType, MedtronicCNLSession pumpSession, byte[] payload) throws EncryptionException {
+    protected MedtronicSendMessage(SendMessageType sendMessageType, MedtronicCnlSession pumpSession, byte[] payload) throws EncryptionException {
         super(CommandType.SEND_MESSAGE, CommandAction.PUMP_REQUEST, pumpSession, buildPayload(sendMessageType, pumpSession, payload));
     }
 
@@ -42,7 +43,7 @@ public class MedtronicSendMessage extends MedtronicMessage {
      * | byte sendSequenceNumber | BE short sendMessageType | byte[] Payload bytes | BE short CCITT CRC |
      * +-------------------------+----------------------+----------------------+--------------------+
      */
-    protected static byte[] buildPayload(SendMessageType sendMessageType, MedtronicCNLSession pumpSession, byte[] payload) throws EncryptionException {
+    protected static byte[] buildPayload(SendMessageType sendMessageType, MedtronicCnlSession pumpSession, byte[] payload) throws EncryptionException {
         byte payloadLength = (byte) (payload == null ? 0 : payload.length);
 
         ByteBuffer sendPayloadBuffer = ByteBuffer.allocate(ENCRYPTED_ENVELOPE_SIZE + payloadLength + CRC_SIZE);
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternRequestMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..d31eb36a229654efedd6b9849fac749c1b748254
--- /dev/null
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternRequestMessage.java
@@ -0,0 +1,12 @@
+package info.nightscout.android.medtronic.message;
+
+import info.nightscout.android.medtronic.MedtronicCnlSession;
+
+/**
+ * Created by lgoedhart on 26/03/2016.
+ */
+public class PumpBasalPatternRequestMessage extends MedtronicSendMessage {
+    public PumpBasalPatternRequestMessage(MedtronicCnlSession pumpSession) throws EncryptionException {
+        super(SendMessageType.READ_BASAL_PATTERN_REQUEST, pumpSession, null);
+    }
+}
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternResponseMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..d4b4ef97d55422a5d5be2a5179879569a59cfcad
--- /dev/null
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternResponseMessage.java
@@ -0,0 +1,21 @@
+package info.nightscout.android.medtronic.message;
+
+import info.nightscout.android.medtronic.MedtronicCnlSession;
+
+/**
+ * Created by lgoedhart on 27/03/2016.
+ */
+public class PumpBasalPatternResponseMessage extends MedtronicReceiveMessage {
+    protected PumpBasalPatternResponseMessage(CommandType commandType, CommandAction commandAction, MedtronicCnlSession pumpSession, byte[] payload) {
+        super(commandType, commandAction, pumpSession, payload);
+    }
+
+    public static ContourNextLinkMessage fromBytes(MedtronicCnlSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException {
+        // TODO - turn this into a factory
+        ContourNextLinkMessage message = MedtronicReceiveMessage.fromBytes(pumpSession, bytes);
+
+        // TODO - Validate the MessageType
+
+        return message;
+    }
+}
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusRequestMessage.java
index aba4dc62d0db5aea298fa4fcc8160b3e7162185a..1162bfe139ca65e06fe1eb95571f1ff67520f78c 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusRequestMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusRequestMessage.java
@@ -1,12 +1,12 @@
 package info.nightscout.android.medtronic.message;
 
-import info.nightscout.android.medtronic.MedtronicCNLSession;
+import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 /**
  * Created by lgoedhart on 26/03/2016.
  */
 public class PumpStatusRequestMessage extends MedtronicSendMessage {
-    public PumpStatusRequestMessage(MedtronicCNLSession pumpSession) throws EncryptionException {
+    public PumpStatusRequestMessage(MedtronicCnlSession pumpSession) throws EncryptionException {
         super(SendMessageType.READ_PUMP_STATUS_REQUEST, pumpSession, null);
     }
 }
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java
index bbd3c3b63c916c6ee20439a83dbc70223d54bf8a..06e9a0f3357d2456f64a5eb7c84ac3218d1e35ee 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java
@@ -1,16 +1,16 @@
 package info.nightscout.android.medtronic.message;
 
-import info.nightscout.android.medtronic.MedtronicCNLSession;
+import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 /**
  * Created by lgoedhart on 27/03/2016.
  */
 public class PumpStatusResponseMessage extends MedtronicReceiveMessage {
-    protected PumpStatusResponseMessage(CommandType commandType, CommandAction commandAction, MedtronicCNLSession pumpSession, byte[] payload) {
+    protected PumpStatusResponseMessage(CommandType commandType, CommandAction commandAction, MedtronicCnlSession pumpSession, byte[] payload) {
         super(commandType, commandAction, pumpSession, payload);
     }
 
-    public static ContourNextLinkMessage fromBytes(MedtronicCNLSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException {
+    public static ContourNextLinkMessage fromBytes(MedtronicCnlSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException {
         // TODO - turn this into a factory
         ContourNextLinkMessage message = MedtronicReceiveMessage.fromBytes(pumpSession, bytes);
 
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeRequestMessage.java
index 97fb82065755fe04806bb1862ea38940c8f1c40d..89305b0a9b3873d3ec4079eeacebf8d740443ffe 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeRequestMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeRequestMessage.java
@@ -1,12 +1,12 @@
 package info.nightscout.android.medtronic.message;
 
-import info.nightscout.android.medtronic.MedtronicCNLSession;
+import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 /**
  * Created by lgoedhart on 26/03/2016.
  */
 public class PumpTimeRequestMessage extends MedtronicSendMessage {
-    public PumpTimeRequestMessage(MedtronicCNLSession pumpSession) throws EncryptionException {
+    public PumpTimeRequestMessage(MedtronicCnlSession pumpSession) throws EncryptionException {
         super(SendMessageType.TIME_REQUEST, pumpSession, null);
     }
 }
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeResponseMessage.java
index 940f9afa38015f6bb9dc185fdd84a28baf4d1a8e..3437dd69c65e9adfe188966ea14cbc1a6f8afb18 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeResponseMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeResponseMessage.java
@@ -1,16 +1,16 @@
 package info.nightscout.android.medtronic.message;
 
-import info.nightscout.android.medtronic.MedtronicCNLSession;
+import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 /**
  * Created by lgoedhart on 27/03/2016.
  */
 public class PumpTimeResponseMessage extends MedtronicReceiveMessage {
-    protected PumpTimeResponseMessage(CommandType commandType, CommandAction commandAction, MedtronicCNLSession pumpSession, byte[] payload) {
+    protected PumpTimeResponseMessage(CommandType commandType, CommandAction commandAction, MedtronicCnlSession pumpSession, byte[] payload) {
         super(commandType, commandAction, pumpSession, payload);
     }
 
-    public static ContourNextLinkMessage fromBytes(MedtronicCNLSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException {
+    public static ContourNextLinkMessage fromBytes(MedtronicCnlSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException {
         // TODO - turn this into a factory
         ContourNextLinkMessage message = MedtronicReceiveMessage.fromBytes(pumpSession, bytes);
 
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ReadInfoResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ReadInfoResponseMessage.java
index 980392929a454a5cd841d74de46f5e46b7669aef..68aab2df7977d4235e1c445b4b8ac0692d16e88b 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/ReadInfoResponseMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/ReadInfoResponseMessage.java
@@ -1,16 +1,16 @@
 package info.nightscout.android.medtronic.message;
 
-import info.nightscout.android.medtronic.MedtronicCNLSession;
+import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 /**
  * Created by lgoedhart on 10/05/2016.
  */
 public class ReadInfoResponseMessage extends MedtronicReceiveMessage {
-    protected ReadInfoResponseMessage(CommandType commandType, CommandAction commandAction, MedtronicCNLSession pumpSession, byte[] payload) {
+    protected ReadInfoResponseMessage(CommandType commandType, CommandAction commandAction, MedtronicCnlSession pumpSession, byte[] payload) {
         super(commandType, commandAction, pumpSession, payload);
     }
 
-    public static ContourNextLinkMessage fromBytes(MedtronicCNLSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException {
+    public static ContourNextLinkMessage fromBytes(MedtronicCnlSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException {
         // TODO - turn this into a factory
         ContourNextLinkMessage message = MedtronicReceiveMessage.fromBytes(pumpSession, bytes);
 
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/RequestLinkKeyResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/RequestLinkKeyResponseMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..423e40fb8ab61296fc9c26995837a56932715a7e
--- /dev/null
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/RequestLinkKeyResponseMessage.java
@@ -0,0 +1,21 @@
+package info.nightscout.android.medtronic.message;
+
+import info.nightscout.android.medtronic.MedtronicCnlSession;
+
+/**
+ * Created by lgoedhart on 10/05/2016.
+ */
+public class RequestLinkKeyResponseMessage extends MedtronicReceiveMessage {
+    protected RequestLinkKeyResponseMessage(CommandType commandType, CommandAction commandAction, MedtronicCnlSession pumpSession, byte[] payload) {
+        super(commandType, commandAction, pumpSession, payload);
+    }
+
+    public static ContourNextLinkMessage fromBytes(MedtronicCnlSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException {
+        // TODO - turn this into a factory
+        ContourNextLinkMessage message = MedtronicReceiveMessage.fromBytes(pumpSession, bytes);
+
+        // TODO - Validate the MessageType
+
+        return message;
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlAlarmReceiver.java b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlAlarmReceiver.java
new file mode 100644
index 0000000000000000000000000000000000000000..49e648c68cc37df7ca643e632c9c76b1457b32f0
--- /dev/null
+++ b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlAlarmReceiver.java
@@ -0,0 +1,78 @@
+package info.nightscout.android.medtronic.service;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.support.v4.content.WakefulBroadcastReceiver;
+import android.util.Log;
+
+import java.util.Date;
+
+/**
+ * Created by lgoedhart on 14/07/2016.
+ */
+public class MedtronicCnlAlarmReceiver extends WakefulBroadcastReceiver {
+    private static final String TAG = MedtronicCnlAlarmReceiver.class.getSimpleName();
+    private static final int ALARM_ID = 102; // Alarm id
+
+    private static PendingIntent pendingIntent = null;
+    private static AlarmManager alarmManager = null;
+
+    @Override
+    public void onReceive(final Context context, Intent intent) {
+        // Start the IntentService
+        Log.d(TAG, "Received broadcast message at " + new Date(System.currentTimeMillis()));
+        Intent service = new Intent(context, MedtronicCnlIntentService.class);
+        startWakefulService(context, service);
+        restartAlarm();
+    }
+
+    public void setContext(Context context) {
+        cancelAlarm();
+
+        alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+        Intent intent = new Intent(context, MedtronicCnlAlarmReceiver.class);
+        pendingIntent = PendingIntent.getBroadcast(context, ALARM_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+
+    // Setting the alarm in 15 seconds from now
+    public void setAlarm() {
+        setAlarm(System.currentTimeMillis());
+    }
+
+    // Setting the alarm to call onRecieve
+    public void setAlarm(long millis) {
+        if (alarmManager == null || pendingIntent == null)
+            return;
+
+        cancelAlarm();
+
+        // don't trigger the past and at least 30 sec away
+        if (millis < System.currentTimeMillis())
+            millis = System.currentTimeMillis();
+
+        Log.d(TAG, "AlarmManager set to fire   at " + new Date(millis));
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(millis, null), pendingIntent);
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            alarmManager.setExact(AlarmManager.RTC_WAKEUP, millis, pendingIntent);
+        } else
+            alarmManager.set(AlarmManager.RTC_WAKEUP, millis, pendingIntent);
+    }
+
+    // restarting the alarm after MedtronicCnlIntentService.POLL_PERIOD_MS from now
+    public void restartAlarm() {
+        setAlarm(System.currentTimeMillis() + MedtronicCnlIntentService.POLL_PERIOD_MS + MedtronicCnlIntentService.POLL_GRACE_PERIOD_MS);
+    }
+
+    // Cancel the alarm.
+    public void cancelAlarm() {
+        if (alarmManager == null || pendingIntent == null)
+            return;
+
+        alarmManager.cancel(pendingIntent);
+    }
+
+}
diff --git a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java
index d39f3246dadb395b61d450476ec7e65335f8eca8..b958a2c3ac004079815a46121313dc9e74cf097a 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java
@@ -1,29 +1,39 @@
 package info.nightscout.android.medtronic.service;
 
+import android.app.AlarmManager;
 import android.app.IntentService;
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
+import android.os.Build;
+import android.preference.PreferenceManager;
 import android.support.v4.app.NotificationManagerCompat;
 import android.support.v4.content.LocalBroadcastManager;
 import android.util.Log;
 
 import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
 import java.util.Date;
 import java.util.Locale;
 import java.util.concurrent.TimeoutException;
 
+import info.nightscout.android.R;
 import info.nightscout.android.USB.UsbHidDriver;
 import info.nightscout.android.medtronic.MainActivity;
-import info.nightscout.android.medtronic.MedtronicCNLReader;
+import info.nightscout.android.medtronic.MedtronicCnlReader;
 import info.nightscout.android.medtronic.message.ChecksumException;
 import info.nightscout.android.medtronic.message.EncryptionException;
 import info.nightscout.android.medtronic.message.MessageUtils;
 import info.nightscout.android.medtronic.message.UnexpectedMessageException;
-import info.nightscout.android.model.CgmStatusEvent;
 import info.nightscout.android.model.medtronicNg.ContourNextLinkInfo;
+import info.nightscout.android.model.medtronicNg.PumpInfo;
+import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
+import info.nightscout.android.upload.nightscout.NightscoutUploadReceiver;
+import info.nightscout.android.xdrip_plus.XDripPlusUploadReceiver;
 import io.realm.Realm;
 import io.realm.RealmResults;
 
@@ -89,7 +99,9 @@ public class MedtronicCnlIntentService extends IntentService {
 
         if (!hasUsbHostFeature()) {
             sendStatus("It appears that this device doesn't support USB OTG.");
-            Log.w(TAG, "Device does not support USB OTG");
+            Log.e(TAG, "Device does not support USB OTG");
+            MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
+            // TODO - throw, don't return
             return;
         }
 
@@ -97,26 +109,34 @@ public class MedtronicCnlIntentService extends IntentService {
         if (cnlStick == null) {
             sendStatus("USB connection error. Is the Bayer Contour Next Link plugged in?");
             Log.w(TAG, "USB connection error. Is the CNL plugged in?");
+
+            // TODO - set status if offline or Nightscout not reachable
+            uploadToNightscout();
+            MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
+            // TODO - throw, don't return
             return;
         }
 
         if (!mUsbManager.hasPermission(UsbHidDriver.getUsbDevice(mUsbManager, USB_VID, USB_PID))) {
             sendMessage(Constants.ACTION_NO_USB_PERMISSION);
+            MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
+            // TODO - throw, don't return
             return;
         }
         mHidDevice = UsbHidDriver.acquire(mUsbManager, cnlStick);
 
-        Realm realm = Realm.getDefaultInstance();
-
         try {
             mHidDevice.open();
         } catch (Exception e) {
             Log.e(TAG, "Unable to open serial device", e);
+            MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
+            // TODO - throw, don't return
             return;
         }
 
-        MedtronicCNLReader cnlReader = new MedtronicCNLReader(mHidDevice);
+        MedtronicCnlReader cnlReader = new MedtronicCnlReader(mHidDevice);
 
+        Realm realm = Realm.getDefaultInstance();
         realm.beginTransaction();
 
         try {
@@ -131,25 +151,14 @@ public class MedtronicCnlIntentService extends IntentService {
                     .findFirst();
 
             if (info == null) {
+                // TODO - use realm.createObject()?
                 info = new ContourNextLinkInfo();
                 info.setSerialNumber(cnlReader.getStickSerial());
 
                 info = realm.copyToRealm(info);
             }
 
-            String hmac = info.getHmac();
-            String key = info.getKey();
-
-            if (hmac == null || key == null) {
-                // Must commit the transaction before we send the Registration activation message
-                realm.commitTransaction();
-
-                sendMessage(Constants.ACTION_USB_REGISTER);
-                return;
-            }
-
-            cnlReader.getPumpSession().setHMAC(MessageUtils.hexStringToByteArray(hmac));
-            cnlReader.getPumpSession().setKey(MessageUtils.hexStringToByteArray(key));
+            cnlReader.getPumpSession().setStickSerial(info.getSerialNumber());
 
             cnlReader.enterControlMode();
 
@@ -157,41 +166,68 @@ public class MedtronicCnlIntentService extends IntentService {
                 cnlReader.enterPassthroughMode();
                 cnlReader.openConnection();
                 cnlReader.requestReadInfo();
-                byte radioChannel = cnlReader.negotiateChannel();
+
+                String key = info.getKey();
+
+                if (key == null) {
+                    cnlReader.requestLinkKey();
+
+                    info.setKey(MessageUtils.byteArrayToHexString(cnlReader.getPumpSession().getKey()));
+                    key = info.getKey();
+                }
+
+                cnlReader.getPumpSession().setKey(MessageUtils.hexStringToByteArray(key));
+
+                long pumpMAC = cnlReader.getPumpSession().getPumpMAC();
+                Log.i(TAG, "PumpInfo MAC: " + (pumpMAC & 0xffffff));
+                MainActivity.setActivePumpMac(pumpMAC);
+                PumpInfo activePump = realm
+                        .where(PumpInfo.class)
+                        .equalTo("pumpMac", pumpMAC)
+                        .findFirst();
+
+                if (activePump == null) {
+                    activePump = realm.createObject(PumpInfo.class);
+                    activePump.setPumpMac(pumpMAC);
+                }
+
+                byte radioChannel = cnlReader.negotiateChannel(activePump.getLastRadioChannel());
                 if (radioChannel == 0) {
                     sendStatus("Could not communicate with the 640g. Are you near the pump?");
                     Log.i(TAG, "Could not communicate with the 640g. Are you near the pump?");
                 } else {
+                    activePump.setLastRadioChannel(radioChannel);
                     sendStatus(String.format(Locale.getDefault(), "Connected to Contour Next Link on channel %d.", (int) radioChannel));
                     Log.d(TAG, String.format("Connected to Contour Next Link on channel %d.", (int) radioChannel));
                     cnlReader.beginEHSMSession();
 
-                    //PumpStatusEvent pumpRecord = realm.createObject(PumpStatusEvent.class);
-                    CgmStatusEvent cgmRecord = realm.createObject(CgmStatusEvent.class);
+                    PumpStatusEvent pumpRecord = realm.createObject(PumpStatusEvent.class);
 
                     String deviceName = String.format("medtronic-640g://%s", cnlReader.getStickSerial());
-                    cgmRecord.setDeviceName(deviceName);
-                    //pumpRecord.setDeviceName(deviceName);
-                    // TODO - legacy. Remove once we've plumbed in pumpRecord.
-                    MainActivity.pumpStatusRecord.setDeviceName(deviceName);
+                    activePump.setDeviceName(deviceName);
+
+                    // TODO - this should not be necessary. We should reverse lookup the device name from PumpInfo
+                    pumpRecord.setDeviceName(deviceName);
 
-                    //pumpRecord.setPumpDate(cnlReader.getPumpTime());
                     long pumpTime = cnlReader.getPumpTime().getTime();
                     long pumpOffset = pumpTime - System.currentTimeMillis();
+                    Log.d(TAG, "Time offset between pump and device: " + pumpOffset + " millis.");
+
                     // TODO - send ACTION to MainActivity to show offset between pump and uploader.
-                    MainActivity.pumpStatusRecord.pumpDate = new Date(pumpTime - pumpOffset);
-                    cnlReader.getPumpStatus(cgmRecord, pumpOffset);
+                    pumpRecord.setPumpTimeOffset(pumpOffset);
+                    pumpRecord.setPumpDate(new Date(pumpTime - pumpOffset));
+                    cnlReader.getPumpStatus(pumpRecord, pumpOffset);
+                    activePump.getPumpHistory().add(pumpRecord);
 
                     cnlReader.endEHSMSession();
 
                     boolean cancelTransaction = true;
-                    if (cgmRecord.getSgv() != 0) {
+                    if (pumpRecord.getSgv() != 0) {
                         // Check that the record doesn't already exist before committing
-                        RealmResults<CgmStatusEvent> checkExistingRecords = realm
-                                .where(CgmStatusEvent.class)
-                                .equalTo("eventDate", cgmRecord.getEventDate())
-                                .equalTo("deviceName", cgmRecord.getDeviceName())
-                                .equalTo("sgv", cgmRecord.getSgv())
+                        RealmResults<PumpStatusEvent> checkExistingRecords = activePump.getPumpHistory()
+                                .where()
+                                .equalTo("eventDate", pumpRecord.getEventDate())
+                                .equalTo("sgv", pumpRecord.getSgv())
                                 .findAll();
 
                         // There should be the 1 record we've already added in this transaction.
@@ -208,12 +244,15 @@ public class MedtronicCnlIntentService extends IntentService {
                         realm.cancelTransaction();
                     }
                 }
-
-                cnlReader.closeConnection();
             } catch (UnexpectedMessageException e) {
                 Log.e(TAG, "Unexpected Message", e);
                 sendStatus("Communication Error: " + e.getMessage());
+            } catch (NoSuchAlgorithmException e) {
+                Log.e(TAG, "Could not determine CNL HMAC", e);
+                sendStatus("Error connecting to Contour Next Link: Hashing error.");
             } finally {
+                //TODO : 05.11.2016 has the close to be here?
+                cnlReader.closeConnection();
                 cnlReader.endPassthroughMode();
                 cnlReader.endControlMode();
             }
@@ -233,13 +272,48 @@ public class MedtronicCnlIntentService extends IntentService {
             Log.e(TAG, "Could not close connection.", e);
             sendStatus("Could not close connection: " + e.getMessage());
         } finally {
-            if (realm.isInTransaction()) {
-                // If we didn't commit the transaction, we've run into an error. Let's roll it back
-                realm.cancelTransaction();
+            if (!realm.isClosed()) {
+                if (realm.isInTransaction()) {
+                    // If we didn't commit the transaction, we've run into an error. Let's roll it back
+                    realm.cancelTransaction();
+                }
+                realm.close();
             }
+
+            // TODO - set status if offline or Nightscout not reachable
+            sendToXDrip();
+            uploadToNightscout();
+            MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
         }
+    }
+
+    // reliable wake alarm manager wake up for all android versions
+    public static void wakeUpIntent(Context context, long wakeTime, PendingIntent pendingIntent) {
+        final AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            alarm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, wakeTime, pendingIntent);
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            alarm.setExact(AlarmManager.RTC_WAKEUP, wakeTime, pendingIntent);
+        } else
+            alarm.set(AlarmManager.RTC_WAKEUP, wakeTime, pendingIntent);
+    }
+
+    private void sendToXDrip() {
+        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+        if (prefs.getBoolean(getString(R.string.preference_enable_xdrip_plus), false)) {
+            final Intent receiverIntent = new Intent(this, XDripPlusUploadReceiver.class);
+            final long timestamp = System.currentTimeMillis() + 500L;
+            final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, (int) timestamp, receiverIntent, PendingIntent.FLAG_ONE_SHOT);
+            Log.d(TAG, "Scheduling xDrip+ send");
+            wakeUpIntent(getApplicationContext(), timestamp, pendingIntent);
+        }
+    }
 
-        realm.close();
+    private void uploadToNightscout() {
+        Intent receiverIntent = new Intent(this, NightscoutUploadReceiver.class);
+        final long timestamp = System.currentTimeMillis() + 1000L;
+        final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, (int) timestamp, receiverIntent, PendingIntent.FLAG_ONE_SHOT);
+        wakeUpIntent(getApplicationContext(), timestamp, pendingIntent);
     }
 
     private boolean hasUsbHostFeature() {
diff --git a/app/src/main/java/info/nightscout/android/model/CgmStatusEvent.java b/app/src/main/java/info/nightscout/android/model/CgmStatusEvent.java
deleted file mode 100644
index 1100d3270879dfb38dd38311cde16f46235899b9..0000000000000000000000000000000000000000
--- a/app/src/main/java/info/nightscout/android/model/CgmStatusEvent.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package info.nightscout.android.model;
-
-import java.util.Date;
-
-import io.realm.RealmObject;
-import io.realm.annotations.Index;
-
-/**
- * Created by lgoedhart on 4/06/2016.
- */
-public class CgmStatusEvent extends RealmObject {
-    @Index
-    private Date eventDate; // The actual time of the event (assume the capture device eventDate/time is accurate)
-    private Date pumpDate; // The eventDate/time on the pump at the time of the event
-    private String deviceName;
-    private int sgv;
-    private String trend;
-    @Index
-    private boolean uploaded = false;
-
-    public Date getEventDate() {
-        return eventDate;
-    }
-
-    public void setEventDate(Date eventDate) {
-        this.eventDate = eventDate;
-    }
-
-    public Date getPumpDate() {
-        return pumpDate;
-    }
-
-    public void setPumpDate(Date pumpDate) {
-        this.pumpDate = pumpDate;
-    }
-
-    public String getDeviceName() {
-        return deviceName;
-    }
-
-    public void setDeviceName(String deviceName) {
-        this.deviceName = deviceName;
-    }
-
-    public int getSgv() {
-        return sgv;
-    }
-
-    public void setSgv(int sgv) {
-        this.sgv = sgv;
-    }
-
-    public TREND getTrend() {
-        return TREND.valueOf(trend);
-    }
-
-    public void setTrend(TREND trend) {
-        this.trend = trend.name();
-    }
-
-    public boolean isUploaded() {
-        return uploaded;
-    }
-
-    public void setUploaded(boolean uploaded) {
-        this.uploaded = uploaded;
-    }
-
-    public enum TREND {
-        NONE,
-        DOUBLE_UP,
-        SINGLE_UP,
-        FOURTY_FIVE_UP,
-        FLAT,
-        FOURTY_FIVE_DOWN,
-        SINGLE_DOWN,
-        DOUBLE_DOWN,
-        NOT_COMPUTABLE,
-        RATE_OUT_OF_RANGE,
-        NOT_SET
-    }
-}
diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/ContourNextLinkInfo.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/ContourNextLinkInfo.java
index 404d801c8250aaa33c13ca53e58b0d9f0e852a05..9b054671a3580a5c5e142405e89d8ea52147e100 100644
--- a/app/src/main/java/info/nightscout/android/model/medtronicNg/ContourNextLinkInfo.java
+++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/ContourNextLinkInfo.java
@@ -9,7 +9,6 @@ import io.realm.annotations.PrimaryKey;
 public class ContourNextLinkInfo extends RealmObject {
     @PrimaryKey
     private String serialNumber;
-    private String hmac;
     private String key;
 
     public String getSerialNumber() {
@@ -20,14 +19,6 @@ public class ContourNextLinkInfo extends RealmObject {
         this.serialNumber = serialNumber;
     }
 
-    public String getHmac() {
-        return hmac;
-    }
-
-    public void setHmac(String hmac) {
-        this.hmac = hmac;
-    }
-
     public String getKey() {
         return key;
     }
diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/Pump.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpInfo.java
similarity index 57%
rename from app/src/main/java/info/nightscout/android/model/medtronicNg/Pump.java
rename to app/src/main/java/info/nightscout/android/model/medtronicNg/PumpInfo.java
index 3d34bef3dceece251d193f1f9b60b6fb4c022d3b..3b25a51612206e6ffc265a1b6e1e18190b056d71 100644
--- a/app/src/main/java/info/nightscout/android/model/medtronicNg/Pump.java
+++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpInfo.java
@@ -1,6 +1,5 @@
 package info.nightscout.android.model.medtronicNg;
 
-import info.nightscout.android.model.CgmStatusEvent;
 import io.realm.RealmList;
 import io.realm.RealmObject;
 import io.realm.annotations.PrimaryKey;
@@ -8,27 +7,35 @@ import io.realm.annotations.PrimaryKey;
 /**
  * Created by lgoedhart on 4/06/2016.
  */
-public class Pump extends RealmObject {
+public class PumpInfo extends RealmObject {
     @PrimaryKey
-    private String serialNumber;
-    private int lastRadioChannel;
+    private long pumpMac;
+    private String deviceName;
+    private byte lastRadioChannel;
     private RealmList<ContourNextLinkInfo> associatedCnls;
-    private RealmList<CgmStatusEvent> cgmHistory;
     private RealmList<PumpStatusEvent> pumpHistory;
 
-    public String getSerialNumber() {
-        return serialNumber;
+    public long getPumpMac() {
+        return pumpMac;
     }
 
-    public void setSerialNumber(String serialNumber) {
-        this.serialNumber = serialNumber;
+    public void setPumpMac(long pumpMac) {
+        this.pumpMac = pumpMac;
     }
 
-    public int getLastRadioChannel() {
+    public String getDeviceName() {
+        return deviceName;
+    }
+
+    public void setDeviceName(String deviceName) {
+        this.deviceName = deviceName;
+    }
+
+    public byte getLastRadioChannel() {
         return lastRadioChannel;
     }
 
-    public void setLastRadioChannel(int lastRadioChannel) {
+    public void setLastRadioChannel(byte lastRadioChannel) {
         this.lastRadioChannel = lastRadioChannel;
     }
 
@@ -40,14 +47,6 @@ public class Pump extends RealmObject {
         this.associatedCnls = associatedCnls;
     }
 
-    public RealmList<CgmStatusEvent> getCgmHistory() {
-        return cgmHistory;
-    }
-
-    public void setCgmHistory(RealmList<CgmStatusEvent> cgmHistory) {
-        this.cgmHistory = cgmHistory;
-    }
-
     public RealmList<PumpStatusEvent> getPumpHistory() {
         return pumpHistory;
     }
@@ -55,4 +54,8 @@ public class Pump extends RealmObject {
     public void setPumpHistory(RealmList<PumpStatusEvent> pumpHistory) {
         this.pumpHistory = pumpHistory;
     }
+
+    public long getPumpSerial() {
+        return pumpMac & 0xffffff;
+    }
 }
diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpStatusEvent.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpStatusEvent.java
index 1a11543456646e2b9040966c3b545085c1f6e704..ab95d50f6d346d506d3a6f03a992e53cfaa00804 100644
--- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpStatusEvent.java
+++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpStatusEvent.java
@@ -3,6 +3,7 @@ package info.nightscout.android.model.medtronicNg;
 import java.util.Date;
 
 import io.realm.RealmObject;
+import io.realm.annotations.Ignore;
 import io.realm.annotations.Index;
 
 /**
@@ -13,11 +14,37 @@ public class PumpStatusEvent extends RealmObject {
     private Date eventDate; // The actual time of the event (assume the capture device eventDate/time is accurate)
     private Date pumpDate; // The eventDate/time on the pump at the time of the event
     private String deviceName;
-    private float activeInsulin;
+
+    // Data from the Medtronic Pump Status message
+    private boolean suspended;
+    private boolean bolusing;
+    private boolean deliveringInsulin;
+    private boolean tempBasalActive;
+    private boolean cgmActive;
+    private byte activeBasalPattern;
+    private float basalRate;
+    private float tempBasalRate;
+    private byte tempBasalPercentage;
+    private short tempBasalMinutesRemaining;
+    private float basalUnitsDeliveredToday;
+    private short batteryPercentage;
     private float reservoirAmount;
+    private short minutesOfInsulinRemaining; // 25h == "more than 1 day"
+    private float activeInsulin;
+    private int sgv;
+    private Date sgvDate;
+    private boolean lowSuspendActive;
+    private String cgmTrend;
+
     private boolean recentBolusWizard; // Whether a bolus wizard has been run recently
     private int bolusWizardBGL; // in mg/dL. 0 means no recent bolus wizard reading.
 
+    @Ignore
+    private long pumpTimeOffset; // millis the pump is ahead
+
+    @Index
+    private boolean uploaded = false;
+
     public Date getEventDate() {
         return eventDate;
     }
@@ -42,6 +69,26 @@ public class PumpStatusEvent extends RealmObject {
         this.deviceName = deviceName;
     }
 
+    public int getSgv() {
+        return sgv;
+    }
+
+    public void setSgv(int sgv) {
+        this.sgv = sgv;
+    }
+
+    public CGM_TREND getCgmTrend() {
+        return CGM_TREND.valueOf(cgmTrend);
+    }
+
+    public void setCgmTrend(CGM_TREND cgmTrend) {
+        this.cgmTrend = cgmTrend.name();
+    }
+
+    public void setCgmTrend(String cgmTrend) {
+        this.cgmTrend = cgmTrend;
+    }
+
     public float getActiveInsulin() {
         return activeInsulin;
     }
@@ -50,6 +97,14 @@ public class PumpStatusEvent extends RealmObject {
         this.activeInsulin = activeInsulin;
     }
 
+    public short getBatteryPercentage() {
+        return batteryPercentage;
+    }
+
+    public void setBatteryPercentage(short batteryPercentage) {
+        this.batteryPercentage = batteryPercentage;
+    }
+
     public float getReservoirAmount() {
         return reservoirAmount;
     }
@@ -58,6 +113,138 @@ public class PumpStatusEvent extends RealmObject {
         this.reservoirAmount = reservoirAmount;
     }
 
+    public boolean hasRecentBolusWizard() {
+        return recentBolusWizard;
+    }
+
+    public int getBolusWizardBGL() {
+        return bolusWizardBGL;
+    }
+
+    public void setBolusWizardBGL(int bolusWizardBGL) {
+        this.bolusWizardBGL = bolusWizardBGL;
+    }
+
+    public boolean isUploaded() {
+        return uploaded;
+    }
+
+    public void setUploaded(boolean uploaded) {
+        this.uploaded = uploaded;
+    }
+
+    public boolean isSuspended() {
+        return suspended;
+    }
+
+    public void setSuspended(boolean suspended) {
+        this.suspended = suspended;
+    }
+
+    public boolean isBolusing() {
+        return bolusing;
+    }
+
+    public void setBolusing(boolean bolusing) {
+        this.bolusing = bolusing;
+    }
+
+    public boolean isDeliveringInsulin() {
+        return deliveringInsulin;
+    }
+
+    public void setDeliveringInsulin(boolean deliveringInsulin) {
+        this.deliveringInsulin = deliveringInsulin;
+    }
+
+    public boolean isTempBasalActive() {
+        return tempBasalActive;
+    }
+
+    public void setTempBasalActive(boolean tempBasalActive) {
+        this.tempBasalActive = tempBasalActive;
+    }
+
+    public boolean isCgmActive() {
+        return cgmActive;
+    }
+
+    public void setCgmActive(boolean cgmActive) {
+        this.cgmActive = cgmActive;
+    }
+
+    public byte getActiveBasalPattern() {
+        return activeBasalPattern;
+    }
+
+    public void setActiveBasalPattern(byte activeBasalPattern) {
+        this.activeBasalPattern = activeBasalPattern;
+    }
+
+    public float getBasalRate() {
+        return basalRate;
+    }
+
+    public void setBasalRate(float basalRate) {
+        this.basalRate = basalRate;
+    }
+
+    public float getTempBasalRate() {
+        return tempBasalRate;
+    }
+
+    public void setTempBasalRate(float tempBasalRate) {
+        this.tempBasalRate = tempBasalRate;
+    }
+
+    public byte getTempBasalPercentage() {
+        return tempBasalPercentage;
+    }
+
+    public void setTempBasalPercentage(byte tempBasalPercentage) {
+        this.tempBasalPercentage = tempBasalPercentage;
+    }
+
+    public short getTempBasalMinutesRemaining() {
+        return tempBasalMinutesRemaining;
+    }
+
+    public void setTempBasalMinutesRemaining(short tempBasalMinutesRemaining) {
+        this.tempBasalMinutesRemaining = tempBasalMinutesRemaining;
+    }
+
+    public float getBasalUnitsDeliveredToday() {
+        return basalUnitsDeliveredToday;
+    }
+
+    public void setBasalUnitsDeliveredToday(float basalUnitsDeliveredToday) {
+        this.basalUnitsDeliveredToday = basalUnitsDeliveredToday;
+    }
+
+    public short getMinutesOfInsulinRemaining() {
+        return minutesOfInsulinRemaining;
+    }
+
+    public void setMinutesOfInsulinRemaining(short minutesOfInsulinRemaining) {
+        this.minutesOfInsulinRemaining = minutesOfInsulinRemaining;
+    }
+
+    public Date getSgvDate() {
+        return sgvDate;
+    }
+
+    public void setSgvDate(Date sgvDate) {
+        this.sgvDate = sgvDate;
+    }
+
+    public boolean isLowSuspendActive() {
+        return lowSuspendActive;
+    }
+
+    public void setLowSuspendActive(boolean lowSuspendActive) {
+        this.lowSuspendActive = lowSuspendActive;
+    }
+
     public boolean isRecentBolusWizard() {
         return recentBolusWizard;
     }
@@ -66,11 +253,25 @@ public class PumpStatusEvent extends RealmObject {
         this.recentBolusWizard = recentBolusWizard;
     }
 
-    public int getBolusWizardBGL() {
-        return bolusWizardBGL;
+    public long getPumpTimeOffset() {
+        return pumpTimeOffset;
     }
 
-    public void setBolusWizardBGL(int bolusWizardBGL) {
-        this.bolusWizardBGL = bolusWizardBGL;
+    public void setPumpTimeOffset(long pumpTimeOffset) {
+        this.pumpTimeOffset = pumpTimeOffset;
+    }
+
+    public enum CGM_TREND {
+        NONE,
+        DOUBLE_UP,
+        SINGLE_UP,
+        FOURTY_FIVE_UP,
+        FLAT,
+        FOURTY_FIVE_DOWN,
+        SINGLE_DOWN,
+        DOUBLE_DOWN,
+        NOT_COMPUTABLE,
+        RATE_OUT_OF_RANGE,
+        NOT_SET
     }
 }
diff --git a/app/src/main/java/info/nightscout/android/settings/SettingsFragment.java b/app/src/main/java/info/nightscout/android/settings/SettingsFragment.java
index e070ac229cd308a3e7f82ae50528aa789bfcbb21..7519a5c7f75cc4e177012ca37b2ec08d272d0b8a 100644
--- a/app/src/main/java/info/nightscout/android/settings/SettingsFragment.java
+++ b/app/src/main/java/info/nightscout/android/settings/SettingsFragment.java
@@ -10,13 +10,7 @@ import android.preference.Preference;
 import android.preference.PreferenceCategory;
 import android.preference.PreferenceFragment;
 
-import java.io.IOException;
-
 import info.nightscout.android.R;
-import info.nightscout.android.upload.nightscout.NightscoutApi;
-import retrofit2.Call;
-import retrofit2.Retrofit;
-import retrofit2.converter.gson.GsonConverterFactory;
 
 public class SettingsFragment extends PreferenceFragment implements OnSharedPreferenceChangeListener {
 
diff --git a/app/src/main/java/info/nightscout/android/upload/DeviceRecord.java b/app/src/main/java/info/nightscout/android/upload/DeviceRecord.java
deleted file mode 100644
index 0d4b5b2739774506e4ae41584a7bc8850905c498..0000000000000000000000000000000000000000
--- a/app/src/main/java/info/nightscout/android/upload/DeviceRecord.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package info.nightscout.android.upload;
-
-import java.io.Serializable;
-
-public class DeviceRecord extends Record implements Serializable{
-	
-	/**
-	 * 
-	 */
-	private static final long serialVersionUID = 6321618305992689901L;
-	
-	public String deviceId = "";
-	
-	protected String deviceName = "";
-	
-	public String getDeviceName(){
-		return deviceName;
-	}
-	public void setDeviceName( String deviceName ){
-		this.deviceName = deviceName;
-	}
-}
diff --git a/app/src/main/java/info/nightscout/android/upload/GlucometerRecord.java b/app/src/main/java/info/nightscout/android/upload/GlucometerRecord.java
deleted file mode 100644
index 10dbb2c22df4d20f73d65a354a7c89f940eae596..0000000000000000000000000000000000000000
--- a/app/src/main/java/info/nightscout/android/upload/GlucometerRecord.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package info.nightscout.android.upload;
-
-import java.io.Serializable;
-
-public class GlucometerRecord  extends Record implements Serializable{
-    public float numGlucometerValue = 0;
-    public long lastDate = 0;
-    
-    private static final long serialVersionUID = 4654897648L;	
-    
-    public void setNumGlucometerValue(int numGlucometerValue) {
-		this.numGlucometerValue = numGlucometerValue;
-	}
-
-	public void setLastDate(long lastDate) {
-		this.lastDate = lastDate;
-	}
-
-}
diff --git a/app/src/main/java/info/nightscout/android/upload/MedtronicNG/CGMRecord.java b/app/src/main/java/info/nightscout/android/upload/MedtronicNG/CGMRecord.java
deleted file mode 100644
index 514c7704cdb501cc2be078510d8fdc64f74ac0d0..0000000000000000000000000000000000000000
--- a/app/src/main/java/info/nightscout/android/upload/MedtronicNG/CGMRecord.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package info.nightscout.android.upload.MedtronicNG;
-
-import info.nightscout.android.upload.DeviceRecord;
-
-import java.io.Serializable;
-import java.util.Date;
-
-/**
- * Created by lgoedhart on 27/03/2016.
- */
-public class CGMRecord extends DeviceRecord implements Serializable {
-    public enum TREND {
-        NONE(0),
-        DOUBLE_UP(1),
-        SINGLE_UP(2),
-        FOURTY_FIVE_UP(3),
-        FLAT(4),
-        FOURTY_FIVE_DOWN(5),
-        SINGLE_DOWN(6),
-        DOUBLE_DOWN(7),
-        NOT_COMPUTABLE(8),
-        RATE_OUT_OF_RANGE(9),
-        NOT_SET(10);
-
-        private byte value;
-        TREND(int trend) {
-           this.value = (byte)trend;
-        }
-    }
-
-    private TREND trend = TREND.NOT_SET;
-
-    public int sgv = 0; // in mg/dL. 0 means no sensor reading
-    public Date sgvDate = new Date();
-    public String direction;
-
-    public void setTrend( TREND trend ) {
-        this.trend = trend;
-
-        switch( trend ) {
-            case NONE:
-                this.direction = "NONE";
-                break;
-            case DOUBLE_UP:
-                this.direction = "DoubleUp";
-                break;
-            case SINGLE_UP:
-                this.direction = "SingleUp";
-                break;
-            case FOURTY_FIVE_UP:
-                this.direction = "FortyFiveUp";
-                break;
-            case FLAT:
-                this.direction = "Flat";
-                break;
-            case FOURTY_FIVE_DOWN:
-                this.direction = "FortyFiveDown";
-                break;
-            case SINGLE_DOWN:
-                this.direction = "SingleDown";
-                break;
-            case DOUBLE_DOWN:
-                this.direction = "DoubleDown";
-                break;
-            case NOT_COMPUTABLE:
-                this.direction = "NOT COMPUTABLE";
-                break;
-            case RATE_OUT_OF_RANGE:
-                this.direction = "RATE OUT OF RANGE";
-                break;
-            case NOT_SET:
-                this.direction = "NONE";
-                break;
-        }
-    }
-
-    public TREND getTrend() {
-        return trend;
-    }
-
-    public static TREND fromMessageByte(byte messageByte) {
-        switch( messageByte ) {
-            case (byte) 0x60:
-                return TREND.FLAT;
-            case (byte) 0xc0:
-                return TREND.DOUBLE_UP;
-            case (byte) 0xa0:
-                return TREND.SINGLE_UP;
-            case (byte) 0x80:
-                return TREND.FOURTY_FIVE_UP;
-            case (byte) 0x40:
-                return TREND.FOURTY_FIVE_DOWN;
-            case (byte) 0x20:
-                return TREND.SINGLE_DOWN;
-            case (byte) 0x00:
-                return TREND.DOUBLE_DOWN;
-            default:
-                return TREND.NOT_COMPUTABLE;
-        }
-    }
-}
diff --git a/app/src/main/java/info/nightscout/android/upload/MedtronicNG/PumpStatusRecord.java b/app/src/main/java/info/nightscout/android/upload/MedtronicNG/PumpStatusRecord.java
deleted file mode 100644
index 8f0c5c239ea3c17f131b2e2088f2bf567dad9f1f..0000000000000000000000000000000000000000
--- a/app/src/main/java/info/nightscout/android/upload/MedtronicNG/PumpStatusRecord.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package info.nightscout.android.upload.MedtronicNG;
-
-import info.nightscout.android.upload.DeviceRecord;
-
-import java.io.Serializable;
-import java.math.BigDecimal;
-import java.util.Date;
-
-/**
- * Created by lgoedhart on 27/03/2016.
- */
-public class PumpStatusRecord extends DeviceRecord implements Serializable {
-    public int batteryPercentage;
-    public Date pumpDate = new Date();
-    public BigDecimal activeInsulin = new BigDecimal(0);
-    public BigDecimal reservoirAmount = new BigDecimal(0);
-    public boolean recentBolusWizard = false; // Whether a bolus wizard has been run recently
-    public int bolusWizardBGL = 0; // in mg/dL. 0 means no recent bolus wizard reading.
-}
diff --git a/app/src/main/java/info/nightscout/android/upload/Record.java b/app/src/main/java/info/nightscout/android/upload/Record.java
deleted file mode 100644
index 61a75faa79a09054a50215cc01f52a65972abb9c..0000000000000000000000000000000000000000
--- a/app/src/main/java/info/nightscout/android/upload/Record.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package info.nightscout.android.upload;
-
-import java.io.Serializable;
-
-public class Record implements Serializable {
-	 public String displayTime = "---";
-
-	/**
-	 * 
-	 */
-	private static final long serialVersionUID = -1381174446348390503L;
-	
-	public void setDisplayTime (String input) {
-    	this.displayTime = input;
-    }
-
-}
diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutApi.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutApi.java
similarity index 100%
rename from app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutApi.java
rename to app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutApi.java
diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadIntentService.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadIntentService.java
index 6ba4b5c8d65b8f71dfa44fcef38cadad02e4c87b..6ac7129878dfdcf45118b3f1f4cf83538766de24 100644
--- a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadIntentService.java
+++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadIntentService.java
@@ -10,6 +10,8 @@ import android.preference.PreferenceManager;
 import android.support.v4.content.LocalBroadcastManager;
 import android.util.Log;
 
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
 import org.apache.http.client.ResponseHandler;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.entity.StringEntity;
@@ -18,8 +20,11 @@ import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.params.BasicHttpParams;
 import org.apache.http.params.HttpConnectionParams;
 import org.apache.http.params.HttpParams;
+import org.json.JSONArray;
 import org.json.JSONObject;
 
+import java.math.BigDecimal;
+import java.net.URL;
 import java.security.MessageDigest;
 import java.text.SimpleDateFormat;
 import java.util.Locale;
@@ -28,7 +33,7 @@ import java.util.regex.Pattern;
 
 import info.nightscout.android.R;
 import info.nightscout.android.medtronic.MainActivity;
-import info.nightscout.android.model.CgmStatusEvent;
+import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
 import info.nightscout.android.upload.nightscout.serializer.EntriesSerializer;
 import io.realm.Realm;
 import io.realm.RealmResults;
@@ -66,28 +71,34 @@ public class NightscoutUploadIntentService extends IntentService {
         Log.d(TAG, "onHandleIntent called");
         mRealm = Realm.getDefaultInstance();
 
-        RealmResults<CgmStatusEvent> records = mRealm
-                .where(CgmStatusEvent.class)
+        RealmResults<PumpStatusEvent> records = mRealm
+                .where(PumpStatusEvent.class)
                 .equalTo("uploaded", false)
                 .notEqualTo("sgv", 0)
                 .findAll();
 
-        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+        if (records.size() > 0) {
+            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
 
-        Boolean enableRESTUpload = prefs.getBoolean("EnableRESTUpload", false);
-        try {
-            if (enableRESTUpload) {
-                long start = System.currentTimeMillis();
-                Log.i(TAG, String.format("Starting upload of %s record using a REST API", records.size()));
-                doRESTUpload(prefs, records);
-                Log.i(TAG, String.format("Finished upload of %s record using a REST API in %s ms", records.size(), System.currentTimeMillis() - start));
+            Boolean enableRESTUpload = prefs.getBoolean("EnableRESTUpload", false);
+            try {
+                if (enableRESTUpload) {
+                    long start = System.currentTimeMillis();
+                    Log.i(TAG, String.format("Starting upload of %s record using a REST API", records.size()));
+                    doRESTUpload(prefs, records);
+                    Log.i(TAG, String.format("Finished upload of %s record using a REST API in %s ms", records.size(), System.currentTimeMillis() - start));
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "ERROR uploading data!!!!!", e);
             }
-        } catch (Exception e) {
-            Log.e(TAG, "ERROR uploading data!!!!!", e);
+        } else {
+            Log.i(TAG, "No records has to be uploaded");
         }
+
+        NightscoutUploadReceiver.completeWakefulIntent(intent);
     }
 
-    private void doRESTUpload(SharedPreferences prefs, RealmResults<CgmStatusEvent> records) {
+    private void doRESTUpload(SharedPreferences prefs, RealmResults<PumpStatusEvent> records) {
         String apiScheme = "https://";
         String apiUrl = "";
         String apiSecret = prefs.getString(mContext.getString(R.string.preference_api_secret), "YOURAPISECRET");
@@ -125,7 +136,7 @@ public class NightscoutUploadIntentService extends IntentService {
         }
     }
 
-    private void doRESTUploadTo(String baseURI, RealmResults<CgmStatusEvent> records) {
+    private void doRESTUploadTo(String baseURI, RealmResults<PumpStatusEvent> records) {
         try {
             String baseURL;
             String secret = null;
@@ -151,128 +162,151 @@ public class NightscoutUploadIntentService extends IntentService {
                 throw new Exception(String.format("Unexpected baseURI: %s, uriParts.length: %s", baseURI, uriParts.length));
             }
 
-            HttpParams params = new BasicHttpParams();
-            HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT);
-            HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
+            JSONArray devicestatusBody = new JSONArray();
+            JSONArray entriesBody = new JSONArray();
 
-            DefaultHttpClient httpclient = new DefaultHttpClient(params);
+            for (PumpStatusEvent record : records) {
+                addDeviceStatus(devicestatusBody, record);
+                addSgvEntry(entriesBody, record);
+                addMbgEntry(entriesBody, record);
+            }
 
-            postDeviceStatus(baseURL, httpclient);
+            boolean isUploaded = uploadToNightscout(new URL(baseURL + "/entries"), secret, entriesBody);
 
-            for (CgmStatusEvent record : records) {
-                String postURL = baseURL + "entries";
+            for(int i = 0; isUploaded && i < devicestatusBody.length(); i++) {
+                isUploaded &= uploadToNightscout(new URL(baseURL + "/devicestatus"), secret, devicestatusBody.getJSONObject(i));
+            }
 
-                Log.i(TAG, "postURL: " + postURL);
+            if (isUploaded) {
+                // Yay! We uploaded. Tell Realm
+                // FIXME - check the upload succeeded!
+                mRealm.beginTransaction();
+                for (PumpStatusEvent updateRecord : records) {
+                    updateRecord.setUploaded(true);
+                }
+                mRealm.commitTransaction();
+            }
 
-                HttpPost post = new HttpPost(postURL);
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to post data", e);
+        }
+    }
 
-                if (secret == null || secret.isEmpty()) {
-                    throw new Exception("Starting with API v1, a pass phase is required");
-                } else {
-                    MessageDigest digest = MessageDigest.getInstance("SHA-1");
-                    byte[] bytes = secret.getBytes("UTF-8");
-                    digest.update(bytes, 0, bytes.length);
-                    bytes = digest.digest();
-                    StringBuilder sb = new StringBuilder(bytes.length * 2);
-                    for (byte b : bytes) {
-                        sb.append(String.format("%02x", b & 0xff));
-                    }
-                    String token = sb.toString();
-                    post.setHeader("api-secret", token);
-                }
+    private boolean uploadToNightscout(URL endpoint, String secret, JSONObject httpBody) throws Exception {
+        return uploadToNightscout(endpoint, secret, httpBody.toString());
+    }
 
-                JSONObject json = new JSONObject();
+    private boolean uploadToNightscout(URL endpoint, String secret, JSONArray httpBody) throws Exception {
+        return uploadToNightscout(endpoint, secret, httpBody.toString());
+    }
 
-                try {
-                    // FIXME - Change this to bulk uploads
-                    populateV1APIEntry(json, record);
-                } catch (Exception e) {
-                    Log.w(TAG, "Unable to populate entry", e);
-                    continue;
-                }
+    private boolean uploadToNightscout(URL endpoint, String secret, String httpBody) throws Exception {
+        Log.i(TAG, "postURL: " + endpoint.toString());
+
+        HttpPost post = new HttpPost(endpoint.toString());
+
+        if (secret == null || secret.isEmpty()) {
+            throw new Exception("Starting with API v1, a pass phase is required");
+        } else {
+            MessageDigest digest = MessageDigest.getInstance("SHA-1");
+            byte[] bytes = secret.getBytes("UTF-8");
+            digest.update(bytes, 0, bytes.length);
+            bytes = digest.digest();
+            StringBuilder sb = new StringBuilder(bytes.length * 2);
+            for (byte b : bytes) {
+                sb.append(String.format("%02x", b & 0xff));
+            }
+            String token = sb.toString();
+            post.setHeader("api-secret", token);
+        }
 
-                String jsonString = json.toString();
+        HttpParams params = new BasicHttpParams();
+        HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT);
+        HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
 
-                Log.i(TAG, "Upload JSON: " + jsonString);
+        DefaultHttpClient httpclient = new DefaultHttpClient(params);
 
-                try {
-                    StringEntity se = new StringEntity(jsonString);
-                    post.setEntity(se);
-                    post.setHeader("Accept", "application/json");
-                    post.setHeader("Content-type", "application/json");
+        Log.i(TAG, "Upload JSON: " + httpBody);
 
-                    ResponseHandler responseHandler = new BasicResponseHandler();
-                    httpclient.execute(post, responseHandler);
-                } catch (Exception e) {
-                    Log.w(TAG, "Unable to post data to: '" + post.getURI().toString() + "'", e);
-                }
+        try {
+            StringEntity se = new StringEntity(httpBody);
+            post.setEntity(se);
+            post.setHeader("Accept", "application/json");
+            post.setHeader("Content-type", "application/json");
 
-                // Yay! We uploaded. Tell Realm
-                mRealm.beginTransaction();
-                // TODO - does realm have auto incrementing keys?
-                // Turns out not yet (https://github.com/realm/realm-java/issues/469),
-                // but in the meantime, use this: https://gist.github.com/carloseduardosx/a7bd88d7337660cd10a2c5dcc580ebd0
-                RealmResults<CgmStatusEvent> updateRecordResults = mRealm
-                        .where(CgmStatusEvent.class)
-                        .equalTo("eventDate", record.getEventDate())
-                        .equalTo("deviceName", record.getDeviceName())
-                        .equalTo("sgv", record.getSgv())
-                        .findAll();
-                // FIXME - We shouldn't need this after we remove insertion of duplicates
-                for (CgmStatusEvent updateRecord : updateRecordResults) {
-                    updateRecord.setUploaded(true);
-                }
-                mRealm.commitTransaction();
-            }
+            ResponseHandler responseHandler = new BasicResponseHandler();
+            httpclient.execute(post, responseHandler);
         } catch (Exception e) {
-            Log.e(TAG, "Unable to post data", e);
+            Log.w(TAG, "Unable to post data to: '" + post.getURI().toString() + "'", e);
+            return false;
         }
-    }
 
-    private void postDeviceStatus(String baseURL, DefaultHttpClient httpclient) throws Exception {
-        String devicestatusURL = baseURL + "devicestatus";
-        Log.i(TAG, "devicestatusURL: " + devicestatusURL);
+        return true;
+    }
 
+    private void addDeviceStatus(JSONArray devicestatusArray, PumpStatusEvent record) throws Exception {
         JSONObject json = new JSONObject();
         json.put("uploaderBattery", MainActivity.batLevel);
-        json.put("device", MainActivity.pumpStatusRecord.getDeviceName());
+        json.put("device", record.getDeviceName());
+        json.put("created_at", ISO8601_DATE_FORMAT.format(record.getPumpDate()));
 
         JSONObject pumpInfo = new JSONObject();
-        pumpInfo.put("clock", ISO8601_DATE_FORMAT.format(MainActivity.pumpStatusRecord.pumpDate));
-        pumpInfo.put("reservoir", MainActivity.pumpStatusRecord.reservoirAmount);
+        pumpInfo.put("clock", ISO8601_DATE_FORMAT.format(record.getPumpDate()));
+        pumpInfo.put("reservoir", new BigDecimal(record.getReservoirAmount()).setScale(3, BigDecimal.ROUND_HALF_UP));
 
         JSONObject iob = new JSONObject();
-        iob.put("timestamp", MainActivity.pumpStatusRecord.pumpDate);
-        iob.put("bolusiob", MainActivity.pumpStatusRecord.activeInsulin);
+        iob.put("timestamp", record.getPumpDate());
+        iob.put("bolusiob", record.getActiveInsulin());
+
+        JSONObject status = new JSONObject();
+        if (record.isBolusing()) {
+            status.put("bolusing", true);
+        } else if (record.isSuspended()) {
+            status.put("suspended", true);
+        } else {
+            status.put("status", "normal");
+        }
 
         JSONObject battery = new JSONObject();
-        battery.put("percent", MainActivity.pumpStatusRecord.batteryPercentage);
+        battery.put("percent", record.getBatteryPercentage());
 
         pumpInfo.put("iob", iob);
         pumpInfo.put("battery", battery);
+        pumpInfo.put("status", status);
+
         json.put("pump", pumpInfo);
         String jsonString = json.toString();
         Log.i(TAG, "Device Status JSON: " + jsonString);
 
-        HttpPost post = new HttpPost(devicestatusURL);
-        StringEntity se = new StringEntity(jsonString);
-        post.setEntity(se);
-        post.setHeader("Accept", "application/json");
-        post.setHeader("Content-type", "application/json");
-
-        ResponseHandler responseHandler = new BasicResponseHandler();
-        httpclient.execute(post, responseHandler);
+        devicestatusArray.put(json);
     }
 
-    private void populateV1APIEntry(JSONObject json, CgmStatusEvent pumpRecord) throws Exception {
+    private void addSgvEntry(JSONArray entriesArray, PumpStatusEvent pumpRecord) throws Exception {
+        JSONObject json = new JSONObject();
         // TODO replace with Retrofit/EntriesSerializer
         json.put("sgv", pumpRecord.getSgv());
-        json.put("direction", EntriesSerializer.getDirectionString(pumpRecord.getTrend()));
+        json.put("direction", EntriesSerializer.getDirectionString(pumpRecord.getCgmTrend()));
         json.put("device", pumpRecord.getDeviceName());
         json.put("type", "sgv");
         json.put("date", pumpRecord.getEventDate().getTime());
         json.put("dateString", pumpRecord.getEventDate());
 
+        entriesArray.put(json);
+    }
+
+    private void addMbgEntry(JSONArray entriesArray, PumpStatusEvent pumpRecord) throws Exception {
+        if (pumpRecord.hasRecentBolusWizard()) {
+            JSONObject json = new JSONObject();
+
+            // TODO replace with Retrofit/EntriesSerializer
+            json.put("type", "mbg");
+            json.put("mbg", pumpRecord.getBolusWizardBGL());
+            json.put("device", pumpRecord.getDeviceName());
+            json.put("date", pumpRecord.getEventDate().getTime());
+            json.put("dateString", pumpRecord.getEventDate());
+
+            entriesArray.put(json);
+        }
     }
 
     private boolean isOnline() {
diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadReceiver.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadReceiver.java
new file mode 100644
index 0000000000000000000000000000000000000000..2eb58caadcb41fbde9bb36e7b9d40778967d1465
--- /dev/null
+++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadReceiver.java
@@ -0,0 +1,21 @@
+package info.nightscout.android.upload.nightscout;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.content.WakefulBroadcastReceiver;
+import android.util.Log;
+
+/**
+ * Created by lgoedhart on 14/07/2016.
+ */
+public class NightscoutUploadReceiver extends WakefulBroadcastReceiver {
+    private static final String TAG = NightscoutUploadReceiver.class.getSimpleName();
+
+    @Override
+    public void onReceive(final Context context, Intent intent) {
+        // Start the IntentService
+        Log.d(TAG, "Received broadcast message");
+        Intent service = new Intent(context, NightscoutUploadIntentService.class);
+        startWakefulService(context, service);
+    }
+}
diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/serializer/EntriesSerializer.java b/app/src/main/java/info/nightscout/android/upload/nightscout/serializer/EntriesSerializer.java
index 02da1b07766c5e7f2a3ce413f791c380ca977d7e..260b8c3b2acab0a24e81dd6c35dee9f9872f4e21 100644
--- a/app/src/main/java/info/nightscout/android/upload/nightscout/serializer/EntriesSerializer.java
+++ b/app/src/main/java/info/nightscout/android/upload/nightscout/serializer/EntriesSerializer.java
@@ -7,13 +7,13 @@ import com.google.gson.JsonSerializer;
 
 import java.lang.reflect.Type;
 
-import info.nightscout.android.model.CgmStatusEvent;
+import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
 
 /**
  * Created by lgoedhart on 26/06/2016.
  */
-public class EntriesSerializer implements JsonSerializer<CgmStatusEvent> {
-    public static String getDirectionString(CgmStatusEvent.TREND trend) {
+public class EntriesSerializer implements JsonSerializer<PumpStatusEvent> {
+    public static String getDirectionString(PumpStatusEvent.CGM_TREND trend) {
         switch( trend ) {
             case NONE:
                 return "NONE";
@@ -43,10 +43,10 @@ public class EntriesSerializer implements JsonSerializer<CgmStatusEvent> {
     }
 
     @Override
-    public JsonElement serialize(CgmStatusEvent src, Type typeOfSrc, JsonSerializationContext context) {
+    public JsonElement serialize(PumpStatusEvent src, Type typeOfSrc, JsonSerializationContext context) {
         final JsonObject jsonObject = new JsonObject();
         jsonObject.addProperty("sgv", src.getSgv());
-        jsonObject.addProperty("direction", getDirectionString(src.getTrend()));
+        jsonObject.addProperty("direction", getDirectionString(src.getCgmTrend()));
         jsonObject.addProperty("device", src.getDeviceName());
         jsonObject.addProperty("type", "sgv");
         jsonObject.addProperty("date", src.getEventDate().getTime());
diff --git a/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadIntentService.java b/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadIntentService.java
new file mode 100644
index 0000000000000000000000000000000000000000..6cc1a6f91b54362685926b06cb6f9072b78704bd
--- /dev/null
+++ b/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadIntentService.java
@@ -0,0 +1,171 @@
+package info.nightscout.android.xdrip_plus;
+
+import android.app.IntentService;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+import java.util.List;
+import java.util.Locale;
+
+import info.nightscout.android.medtronic.MainActivity;
+import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
+import info.nightscout.android.upload.nightscout.serializer.EntriesSerializer;
+import io.realm.Realm;
+import io.realm.RealmResults;
+import io.realm.Sort;
+
+/**
+ * Created by jamorham on 17/11/2016.
+ */
+
+
+public class XDripPlusUploadIntentService extends IntentService {
+
+    private static final String TAG = XDripPlusUploadIntentService.class.getSimpleName();
+    private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
+    Context mContext;
+    private Realm mRealm;
+
+    public XDripPlusUploadIntentService() {
+        super(XDripPlusUploadIntentService.class.getName());
+    }
+
+    // status unused
+    protected void sendStatus(String message) {
+        Intent localIntent =
+                new Intent(info.nightscout.android.xdrip_plus.XDripPlusUploadIntentService.Constants.ACTION_STATUS_MESSAGE)
+                        .putExtra(info.nightscout.android.xdrip_plus.XDripPlusUploadIntentService.Constants.EXTENDED_DATA, message);
+        LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        Log.i(TAG, "onCreate called");
+        mContext = this.getBaseContext();
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        Log.d(TAG, "onHandleIntent called");
+        mRealm = Realm.getDefaultInstance();
+
+        RealmResults<PumpStatusEvent> all_records = mRealm
+                .where(PumpStatusEvent.class)
+                .notEqualTo("sgv", 0)
+                .findAllSorted("eventDate", Sort.DESCENDING);
+
+        // get the most recent record and send that
+        if (all_records.size() > 0) {
+            List<PumpStatusEvent> records = all_records.subList(0, 1);
+            doXDripUpload(records);
+        }
+        XDripPlusUploadReceiver.completeWakefulIntent(intent);
+    }
+
+    private void doXDripUpload(List<PumpStatusEvent> records) {
+        try {
+
+            final JSONArray devicestatusBody = new JSONArray();
+            final JSONArray entriesBody = new JSONArray();
+
+            for (PumpStatusEvent record : records) {
+                addDeviceStatus(devicestatusBody, record);
+                addSgvEntry(entriesBody, record);
+                addMbgEntry(entriesBody, record);
+            }
+
+            if (entriesBody.length() > 0) sendBundle(mContext, "add", "entries", entriesBody);
+            if (devicestatusBody.length() > 0)
+                sendBundle(mContext, "add", "devicestatus", devicestatusBody);
+
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to send bundle: " + e);
+        }
+    }
+
+    private void sendBundle(Context context, String action, String collection, JSONArray json) {
+        final Bundle bundle = new Bundle();
+        bundle.putString("action", action);
+        bundle.putString("collection", collection);
+        bundle.putString("data", json.toString());
+        final Intent intent = new Intent(Constants.XDRIP_PLUS_NS_EMULATOR);
+        intent.putExtras(bundle).addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+        context.sendBroadcast(intent);
+        List<ResolveInfo> receivers = context.getPackageManager().queryBroadcastReceivers(intent, 0);
+        if (receivers.size() < 1) {
+            Log.e(TAG, "No receivers");
+        } else Log.e(TAG, receivers.size() + " receivers");
+    }
+
+
+    private void addDeviceStatus(JSONArray devicestatusArray, PumpStatusEvent record) throws Exception {
+        JSONObject json = new JSONObject();
+        json.put("uploaderBattery", MainActivity.batLevel);
+        json.put("device", record.getDeviceName());
+        json.put("created_at", ISO8601_DATE_FORMAT.format(record.getPumpDate()));
+
+        JSONObject pumpInfo = new JSONObject();
+        pumpInfo.put("clock", ISO8601_DATE_FORMAT.format(record.getPumpDate()));
+        pumpInfo.put("reservoir", new BigDecimal(record.getReservoirAmount()).setScale(3, BigDecimal.ROUND_HALF_UP));
+
+        JSONObject iob = new JSONObject();
+        iob.put("timestamp", record.getPumpDate());
+        iob.put("bolusiob", record.getActiveInsulin());
+
+        JSONObject battery = new JSONObject();
+        battery.put("percent", record.getBatteryPercentage());
+
+        pumpInfo.put("iob", iob);
+        pumpInfo.put("battery", battery);
+        json.put("pump", pumpInfo);
+        //String jsonString = json.toString();
+
+        devicestatusArray.put(json);
+    }
+
+    private void addSgvEntry(JSONArray entriesArray, PumpStatusEvent pumpRecord) throws Exception {
+        JSONObject json = new JSONObject();
+        // TODO replace with Retrofit/EntriesSerializer
+        json.put("sgv", pumpRecord.getSgv());
+        json.put("direction", EntriesSerializer.getDirectionString(pumpRecord.getCgmTrend()));
+        json.put("device", pumpRecord.getDeviceName());
+        json.put("type", "sgv");
+        json.put("date", pumpRecord.getEventDate().getTime());
+        json.put("dateString", pumpRecord.getEventDate());
+
+        entriesArray.put(json);
+    }
+
+    private void addMbgEntry(JSONArray entriesArray, PumpStatusEvent pumpRecord) throws Exception {
+        if (pumpRecord.hasRecentBolusWizard()) {
+            JSONObject json = new JSONObject();
+
+            // TODO replace with Retrofit/EntriesSerializer
+            json.put("type", "mbg");
+            json.put("mbg", pumpRecord.getBolusWizardBGL());
+            json.put("device", pumpRecord.getDeviceName());
+            json.put("date", pumpRecord.getEventDate().getTime());
+            json.put("dateString", pumpRecord.getEventDate());
+
+            entriesArray.put(json);
+        }
+    }
+
+
+    public final class Constants {
+        public static final String ACTION_STATUS_MESSAGE = "info.nightscout.android.xdrip_plus.STATUS_MESSAGE";
+        public static final String EXTENDED_DATA = "info.nightscout.android.xdrip_plus.DATA";
+        private static final String XDRIP_PLUS_NS_EMULATOR = "com.eveningoutpost.dexdrip.NS_EMULATOR";
+    }
+}
diff --git a/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadReceiver.java b/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadReceiver.java
new file mode 100644
index 0000000000000000000000000000000000000000..734cd34d65542d827f26d5b81290ccb42023db47
--- /dev/null
+++ b/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadReceiver.java
@@ -0,0 +1,22 @@
+package info.nightscout.android.xdrip_plus;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.content.WakefulBroadcastReceiver;
+import android.util.Log;
+
+
+/**
+ * Created by jamorham on 17/11/2016.
+ */
+public class XDripPlusUploadReceiver extends WakefulBroadcastReceiver {
+    private static final String TAG = XDripPlusUploadReceiver.class.getSimpleName();
+
+    @Override
+    public void onReceive(final Context context, Intent intent) {
+        // Start the IntentService
+        Log.d(TAG, "Received broadcast message");
+        Intent service = new Intent(context, XDripPlusUploadIntentService.class);
+        startWakefulService(context, service);
+    }
+}
diff --git a/app/src/main/res/drawable/drawer_header.jpg b/app/src/main/res/drawable/drawer_header.jpg
index e3f8ab881ebc761afbfdad7ae6b554adb658d83a..251abd5c8a8ee193ef484393a966c891c8f5478a 100644
Binary files a/app/src/main/res/drawable/drawer_header.jpg and b/app/src/main/res/drawable/drawer_header.jpg differ
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index 56ac4a9c6e3a449922fac8c1bcedbd368be5f4a1..84cc07b2b8603da6a558869fe53fa029ae140bcd 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -11,13 +11,6 @@
     tools:context=".medtronic.GetHmacAndKeyActivity">
 
     <!-- Login progress -->
-    <ProgressBar
-        android:id="@+id/login_progress"
-        style="?android:attr/progressBarStyleLarge"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="8dp"
-        android:visibility="gone"/>
 
     <ScrollView
         android:layout_width="match_parent"
@@ -28,52 +21,10 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content">
 
-            <LinearLayout
-                android:id="@+id/login_form"
+            <TextView
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:orientation="vertical">
-
-                <TextView
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:textAppearance="?android:attr/textAppearanceMedium"
-                    android:text="@string/prompt_carelink_username_password" />
-
-                <AutoCompleteTextView
-                    android:id="@+id/username"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:hint="@string/prompt_username"
-                    android:inputType="text"
-                    android:imeOptions="actionNext"
-                    android:maxLines="1"
-                    android:singleLine="true"/>
-
-                <EditText
-                    android:id="@+id/password"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:hint="@string/prompt_password"
-                    android:imeActionId="@+id/login"
-                    android:imeActionLabel="@string/action_sign_in_short"
-                    android:imeOptions="actionDone|actionUnspecified"
-                    android:inputType="textPassword"
-                    android:maxLines="1"
-                    android:singleLine="true"/>
-
-            </LinearLayout>
-
-            <LinearLayout
-                android:orientation="horizontal"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent">
-
-                <TextView
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:id="@+id/registered_usb_devices" />
-            </LinearLayout>
+                android:id="@+id/registered_usb_devices" />
 
         </LinearLayout>
     </ScrollView>
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index b8e7b4b93be8c470d57fcb88bf2d70fa92a9fed9..af14d6804fd460697687fa207ac50f610a4bf518 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -119,13 +119,11 @@
 
     </LinearLayout>
 
-    <!--
     <com.github.mikephil.charting.charts.LineChart
         android:id="@+id/chart"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:visibility="invisible" />
-    -->
 
     <ScrollView
         android:id="@+id/scrollView"
diff --git a/app/src/main/res/menu/menu_register_usb.xml b/app/src/main/res/menu/menu_register_usb.xml
deleted file mode 100644
index 81a6c5646903e8d5f192edbd9d651e4b025c70dc..0000000000000000000000000000000000000000
--- a/app/src/main/res/menu/menu_register_usb.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
-    <item android:id="@+id/action_menu_login"
-        android:title="Login"
-        android:orderInCategory="100"
-        app:showAsAction="always"
-        />
-</menu>
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index bbb585428d3ae24f746f6ff16c7ccdb68440663e..443d000163d74d1afe620bd3cb389b7d005b04b1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -30,7 +30,7 @@
     <string name="preference_api_secret">API SECRET</string>
     <string name="prompt_carelink_username_password">Please enter your CareLink details.\nThey will not be stored.</string>
     <string name="close">Close</string>
-    <string name="register_contour_next_link">Register Contour Next Link</string>
+    <string name="register_contour_next_link">Registered Devices</string>
     <string name="preferences_enable_crashlytics">prefs_enable_crashlytics</string>
     <string name="preferences_enable_answers">prefs_enable_answers</string>
     <string name="preferences_enable_remote_logcat">prefs_enable_remote_logcat</string>
@@ -41,6 +41,7 @@
     <string name="button_text_start_uploading_data">Start Uploading CGM Data</string>
     <string name="preference_eula_accepted">IUNDERSTAND</string>
     <string name="preference_enable_rest_upload">EnableRESTUpload</string>
+    <string name="preference_enable_xdrip_plus">EnablexDripPlusUpload</string>
     <string name="error_msg_api_secret_length">API Secret must be 12 characters or longer.</string>
     <string name="text_unit_mmolxl">mmol/L</string>
     <string name="text_unit_mgxdl">mg/dL</string>
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 8a8fb08d3852284649d8428d84e72fac42c4d557..9e6793c85ec60101ef37b053d3af5d9caefd74b1 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -39,6 +39,10 @@
             android:dialogTitle="Enter your Nightscout API secret"
             android:key="@string/preference_api_secret"
             android:title="API Secret"/>
+        <CheckBoxPreference
+            android:key="@string/preference_enable_xdrip_plus"
+            android:summary="Enable local broadcast of data to xDrip+"
+            android:title="Send to xDrip+"/>
     </PreferenceCategory>
     <PreferenceCategory android:title="Disclaimer">
         <SwitchPreference
diff --git a/build.gradle b/build.gradle
index c08cd482b77a2b4b7463d7023f54a0d5f4adb69f..9da42b0b994170c75de5ee547bfabc0007cf8dd7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,11 +1,11 @@
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
+    // Top-level build file where you can add configuration options common to all sub-projects/modules.
 
 buildscript {
     repositories {
         jcenter()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.1.0'
+        classpath 'com.android.tools.build:gradle:2.2.3'
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
         classpath "io.realm:realm-gradle-plugin:1.0.0"
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 52e3cb73d4fabf8cfd57d817bcf31c28a211bdf1..3d5435e07e9964062e47fc0426eaa27552a17041 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Sun May 01 23:04:04 AEST 2016
+#Sat Nov 12 11:44:13 AEDT 2016
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip