diff --git a/app/build.gradle b/app/build.gradle
index 13710f83b2adda6231962da4fd5883ef4fe80932..a8b8c6dadedc0cf41970a355f31ff8b7a0592d5f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -51,8 +51,15 @@ def getBugfenderApiKey() {
     String bugfenderApiKey = System.getenv("BUGFENDER_API_KEY")
 
     if(bugfenderApiKey == null) {
-        logger.warn("Bugfender API key not set")
-        bugfenderApiKey = ""
+        File file = new File("app/bugfender.properties")
+        if (file.exists()) {
+            Properties properties = new Properties()
+            properties.load(new FileInputStream(file.getAbsolutePath().toString()))
+            bugfenderApiKey = properties.getProperty("apiKey", "")
+        } else {
+            logger.warn("Bugfender API key not set")
+            bugfenderApiKey = ""
+        }
     }
 
     return "\"" + bugfenderApiKey + "\""
diff --git a/app/src/main/java/info/nightscout/android/UploaderApplication.java b/app/src/main/java/info/nightscout/android/UploaderApplication.java
index 1bfc87f81ded988434835da9d0af962dce3ff079..28e99ae0d3ab9b10804f42181ffd84bc7b273c1a 100644
--- a/app/src/main/java/info/nightscout/android/UploaderApplication.java
+++ b/app/src/main/java/info/nightscout/android/UploaderApplication.java
@@ -8,9 +8,15 @@ import com.bugfender.sdk.Bugfender;
 import com.crashlytics.android.Crashlytics;
 import com.crashlytics.android.answers.Answers;
 
+import info.nightscout.android.model.medtronicNg.BasalRate;
+import info.nightscout.android.model.medtronicNg.BasalSchedule;
+import info.nightscout.android.model.medtronicNg.ContourNextLinkInfo;
+import info.nightscout.android.model.medtronicNg.PumpInfo;
+import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
 import io.fabric.sdk.android.Fabric;
 import io.realm.Realm;
 import io.realm.RealmConfiguration;
+import io.realm.annotations.RealmModule;
 import uk.co.chrisjenx.calligraphy.CalligraphyConfig;
 
 /**
@@ -43,9 +49,15 @@ public class UploaderApplication extends Application {
 
         Realm.init(this);
         RealmConfiguration realmConfiguration = new RealmConfiguration.Builder()
+                .modules(new MainModule())
                 .deleteRealmIfMigrationNeeded()
                 .build();
 
         Realm.setDefaultConfiguration(realmConfiguration);
     }
+
+    @RealmModule(classes = {BasalRate.class, BasalSchedule.class, ContourNextLinkInfo.class, PumpInfo.class, PumpStatusEvent.class})
+    public class MainModule {
+    }
+
 }
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 f0b9dd1aa68aa5965e29420cd5c23df5870f20a9..e458cdbb43aab20544dd8ef25a17bdf4cc3f5c47 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java
@@ -64,8 +64,6 @@ import java.text.NumberFormat;
 import java.text.SimpleDateFormat;
 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;
@@ -73,15 +71,16 @@ import info.nightscout.android.eula.Eula;
 import info.nightscout.android.eula.Eula.OnEulaAgreedTo;
 import info.nightscout.android.medtronic.service.MedtronicCnlAlarmManager;
 import info.nightscout.android.medtronic.service.MedtronicCnlIntentService;
-import info.nightscout.android.model.medtronicNg.PumpInfo;
 import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
 import info.nightscout.android.settings.SettingsActivity;
 import info.nightscout.android.utils.ConfigurationStore;
 import info.nightscout.android.utils.DataStore;
+import info.nightscout.android.utils.StatusStore;
 import io.realm.Realm;
-import io.realm.RealmChangeListener;
+import io.realm.RealmConfiguration;
 import io.realm.RealmResults;
 import io.realm.Sort;
+import io.realm.annotations.RealmModule;
 import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper;
 
 public class MainActivity extends AppCompatActivity implements OnSharedPreferenceChangeListener, OnEulaAgreedTo {
@@ -97,14 +96,20 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
 
     private boolean mEnableCgmService = true;
     private SharedPreferences prefs = null;
-    private PumpInfo mActivePump;
     private TextView mTextViewLog; // This will eventually move to a status page.
+    private TextView mTextViewLogButtonTop;
+    private TextView mTextViewLogButtonTopRecent;
+    private TextView mTextViewLogButtonBottom;
+    private TextView mTextViewLogButtonBottomRecent;
+
     private ScrollView mScrollView;
     private GraphView mChart;
     private Handler mUiRefreshHandler = new Handler();
     private Runnable mUiRefreshRunnable = new RefreshDisplayRunnable();
     private Realm mRealm;
+    private Realm storeRealm;
     private StatusMessageReceiver statusMessageReceiver = new StatusMessageReceiver();
+    private UpdatePumpReceiver updatePumpReceiver = new UpdatePumpReceiver();
     private UsbReceiver usbReceiver = new UsbReceiver();
     private BatteryReceiver batteryReceiver = new BatteryReceiver();
 
@@ -117,35 +122,8 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
     }
 
-    /**
-     * calculate the next poll timestamp based on last svg event
-     *
-     * @param pumpStatusData
-     * @return timestamp
-     */
-    public static long getNextPoll(PumpStatusEvent pumpStatusData) {
-        long nextPoll = pumpStatusData.getSgvDate().getTime(),
-                now = System.currentTimeMillis(),
-                pollInterval = ConfigurationStore.getInstance().getPollInterval();
-
-        // align to next poll slot
-        if (nextPoll + 2 * 60 * 60 * 1000 < now) { // last event more than 2h old -> could be a calibration
-            nextPoll = System.currentTimeMillis() + 1000;
-        } else {
-            // align to poll interval
-            nextPoll += (((now - nextPoll) / pollInterval)) * pollInterval
-                    + MedtronicCnlIntentService.POLL_GRACE_PERIOD_MS;
-            if (pumpStatusData.getBatteryPercentage() > 25) {
-                // poll every 5 min
-                nextPoll += pollInterval;
-            } else {
-                // if pump battery seems to be empty reduce polling to save battery (every 15 min)
-                //TODO add message & document it
-                nextPoll += ConfigurationStore.getInstance().getLowBatteryPollInterval();
-            }
-        }
-
-        return nextPoll;
+    @RealmModule(classes = {StatusStore.class})
+    private class StoreModule {
     }
 
     @Override
@@ -155,10 +133,12 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
 
         mRealm = Realm.getDefaultInstance();
 
-        RealmResults<PumpStatusEvent> data = mRealm.where(PumpStatusEvent.class)
-                .findAllSorted("eventDate", Sort.DESCENDING);
-        if (data.size() > 0)
-            dataStore.setLastPumpStatus(data.first());
+        RealmConfiguration realmConfiguration = new RealmConfiguration.Builder()
+                .name("storerealm.realm")
+                .modules(new StoreModule())
+                .deleteRealmIfMigrationNeeded()
+                .build();
+        storeRealm = Realm.getInstance(realmConfiguration);
 
         setContentView(R.layout.activity_main);
 
@@ -204,7 +184,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                 statusMessageReceiver,
                 new IntentFilter(MedtronicCnlIntentService.Constants.ACTION_STATUS_MESSAGE));
         LocalBroadcastManager.getInstance(this).registerReceiver(
-                new UpdatePumpReceiver(),
+                updatePumpReceiver,
                 new IntentFilter(MedtronicCnlIntentService.Constants.ACTION_UPDATE_PUMP));
 
         mEnableCgmService = Eula.show(this, prefs);
@@ -228,7 +208,6 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                 new IntentFilter(MedtronicCnlIntentService.Constants.ACTION_USB_REGISTER));
 
         Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
-
         if (toolbar != null) {
             setSupportActionBar(toolbar);
             getSupportActionBar().setDisplayHomeAsUpEnabled(false);
@@ -315,6 +294,39 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         mTextViewLog = (TextView) findViewById(R.id.textview_log);
         mScrollView = (ScrollView) findViewById(R.id.scrollView);
         mScrollView.setSmoothScrollingEnabled(true);
+        mTextViewLogButtonTop = (TextView) findViewById(R.id.button_log_top);
+        mTextViewLogButtonTop.setVisibility(View.GONE);
+        mTextViewLogButtonTop.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                statusMessageReceiver.changeStatusViewOlder();
+            }
+        });
+        mTextViewLogButtonTopRecent = (TextView) findViewById(R.id.button_log_top_recent);
+        mTextViewLogButtonTopRecent.setVisibility(View.GONE);
+        mTextViewLogButtonTopRecent.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                statusMessageReceiver.changeStatusViewRecent();
+            }
+        });
+
+        mTextViewLogButtonBottom = (TextView) findViewById(R.id.button_log_bottom);
+        mTextViewLogButtonBottom.setVisibility(View.GONE);
+        mTextViewLogButtonBottom.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                statusMessageReceiver.changeStatusViewNewer();
+            }
+        });
+        mTextViewLogButtonBottomRecent = (TextView) findViewById(R.id.button_log_bottom_recent);
+        mTextViewLogButtonBottomRecent.setVisibility(View.GONE);
+        mTextViewLogButtonBottomRecent.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                statusMessageReceiver.changeStatusViewRecent();
+            }
+        });
 
         mChart = (GraphView) findViewById(R.id.chart);
 
@@ -468,7 +480,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
 
     private void refreshDisplay() {
         cancelDisplayRefreshLoop();
-        mUiRefreshHandler.post(mUiRefreshRunnable);;
+        mUiRefreshHandler.post(mUiRefreshRunnable);
     }
     private void refreshDisplay(int delay) {
         cancelDisplayRefreshLoop();
@@ -488,20 +500,28 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
     }
 
     private void startCgmServiceDelayed(long delay) {
+        long now = System.currentTimeMillis();
+        long start = now + 1000;
+
         if (!mRealm.isClosed()) {
+
             RealmResults<PumpStatusEvent> results = mRealm.where(PumpStatusEvent.class)
-                    .findAllSorted("eventDate", Sort.DESCENDING);
+                    .greaterThan("eventDate", new Date(System.currentTimeMillis() - (24 * 60 * 1000)))
+                    .equalTo("validCGM", true)
+                    .findAllSorted("cgmDate", Sort.DESCENDING);
+
             if (results.size() > 0) {
-                long nextPoll = getNextPoll(results.first());
-                long pollInterval = results.first().getBatteryPercentage() > 25 ? ConfigurationStore.getInstance().getPollInterval() : ConfigurationStore.getInstance().getLowBatteryPollInterval();
-                if ((nextPoll - MedtronicCnlIntentService.POLL_GRACE_PERIOD_MS - results.first().getSgvDate().getTime()) <= pollInterval) {
-                    startCgmService(nextPoll + delay);
-                    sendStatus("Next poll due at: " + dateFormatter.format(nextPoll + delay));
-                    return;
-                }
+                long timeLastCGM = results.first().getCgmDate().getTime();
+                if (now - timeLastCGM <  MedtronicCnlIntentService.POLL_GRACE_PERIOD_MS + MedtronicCnlIntentService.POLL_PERIOD_MS)
+                    start = timeLastCGM + MedtronicCnlIntentService.POLL_GRACE_PERIOD_MS + MedtronicCnlIntentService.POLL_PERIOD_MS;
             }
         }
-        startCgmService(System.currentTimeMillis() + (delay == 0 ? 1000 : delay));
+
+        if (start - now < delay) start = now + delay;
+        startCgmService(start);
+
+        if (start - now > 10 * 1000)
+            sendStatus("Next poll due at: " + dateFormatter.format(start));
     }
 
     private void startCgmService(long initialPoll) {
@@ -559,24 +579,30 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         Log.i(TAG, "onResume called");
         super.onResume();
         // Focus status log to most recent on returning to app
-        mScrollView.post(new Runnable() {
-            public void run() {
-                mScrollView.fullScroll(View.FOCUS_DOWN);
-            }
-        });
+        statusMessageReceiver.changeStatusViewRecent();
     }
 
     @Override
     protected void onDestroy() {
         Log.i(TAG, "onDestroy called");
+        statusMessageReceiver.addMessage(MedtronicCnlIntentService.ICON_INFO + "Shutting down uploader.");
+        statusMessageReceiver.addMessage("-----------------------------------------------------");
+
         super.onDestroy();
 
+        cancelDisplayRefreshLoop();
+
+        LocalBroadcastManager.getInstance(this).unregisterReceiver(statusMessageReceiver);
+        LocalBroadcastManager.getInstance(this).unregisterReceiver(updatePumpReceiver);
+        LocalBroadcastManager.getInstance(this).unregisterReceiver(usbReceiver);
         unregisterReceiver(usbReceiver);
         unregisterReceiver(batteryReceiver);
 
         PreferenceManager.getDefaultSharedPreferences(getBaseContext()).unregisterOnSharedPreferenceChangeListener(this);
-        cancelDisplayRefreshLoop();
 
+        if (!storeRealm.isClosed()) {
+            storeRealm.close();
+        }
         if (!mRealm.isClosed()) {
             mRealm.close();
         }
@@ -635,77 +661,6 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         startActivity(manageCNLIntent);
     }
 
-    private PumpInfo getActivePump() {
-        long activePumpMac = dataStore.getActivePumpMac();
-        if (activePumpMac != 0L && (mActivePump == null || !mActivePump.isValid() || mActivePump.getPumpMac() != activePumpMac)) {
-            if (mActivePump != null) {
-                // remove listener on old pump
-                mRealm.executeTransaction(new Realm.Transaction() {
-                    @Override
-                    public void execute(Realm sRealm) {
-                        mActivePump.removeAllChangeListeners();
-                    }
-                });
-                mActivePump = null;
-            }
-
-            PumpInfo pump = mRealm
-                    .where(PumpInfo.class)
-                    .equalTo("pumpMac", activePumpMac)
-                    .findFirst();
-
-            if (pump != null && pump.isValid()) {
-
-                // first change listener start can miss fresh data and not update until next poll, force a refresh now
-                RemoveOutdatedRecords();
-                refreshDisplay(1000);
-
-                mActivePump = pump;
-                mActivePump.addChangeListener(new RealmChangeListener<PumpInfo>() {
-                    long lastQueryTS = 0;
-
-                    @Override
-                    public void onChange(PumpInfo pump) {
-                        // prevent double updating after deleting old events below
-                        if (pump.getLastQueryTS() == lastQueryTS || !pump.isValid()) {
-                            return;
-                        }
-
-                        lastQueryTS = pump.getLastQueryTS();
-
-                        RemoveOutdatedRecords();
-                        refreshDisplay(1000);
-
-                        // TODO - handle isOffline in NightscoutUploadIntentService?
-                    }
-                });
-            }
-        }
-
-        return mActivePump;
-    }
-
-    private void RemoveOutdatedRecords() {
-        // Delete invalid or old records from Realm
-        // TODO - show an error message if the valid records haven't been uploaded
-        final RealmResults<PumpStatusEvent> results =
-                mRealm.where(PumpStatusEvent.class)
-                        .equalTo("sgv", 0)
-                        .or()
-                        .lessThan("sgvDate", new Date(System.currentTimeMillis() - (24 * 60 * 60 * 1000)))
-                        .findAll();
-        if (results.size() > 0) {
-            mRealm.executeTransaction(new Realm.Transaction() {
-                @Override
-                public void execute(Realm realm) {
-                    // Delete all matches
-                    Log.d(TAG, "Deleting " + results.size() + " records from realm");
-                    results.deleteAllFromRealm();
-                }
-            });
-        }
-    }
-
     public static String strFormatSGV(double sgvValue) {
         ConfigurationStore configurationStore = ConfigurationStore.getInstance();
 
@@ -723,87 +678,166 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         }
     }
 
-    public static String renderTrendSymbol(PumpStatusEvent.CGM_TREND trend) {
-        // TODO - symbols used for trend arrow may vary per device, find a more robust solution
-        switch (trend) {
-            case DOUBLE_UP:
-                return "\u21c8";
-            case SINGLE_UP:
-                return "\u2191";
-            case FOURTY_FIVE_UP:
-                return "\u2197";
-            case FLAT:
-                return "\u2192";
-            case FOURTY_FIVE_DOWN:
-                return "\u2198";
-            case SINGLE_DOWN:
-                return "\u2193";
-            case DOUBLE_DOWN:
-                return "\u21ca";
-            default:
-                return "\u2014";
-        }
-    }
-
     private class StatusMessageReceiver extends BroadcastReceiver {
-        private class StatusMessage {
-            private long timestamp;
-            private String message;
+        private static final int PAGE_SIZE = 300;
+        private static final int FIRSTPAGE_SIZE = 100;
+        private static final int STALE_MS = 72 * 60 * 60 * 1000;
+        private int viewPosition = 0;
+        private int viewPositionSecondPage = 0;
 
-            StatusMessage(String message) {
-                this(System.currentTimeMillis(), message);
-            }
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String message = intent.getStringExtra(MedtronicCnlIntentService.Constants.EXTENDED_DATA);
+            Log.i(TAG, "Message Receiver: " + message);
+            addMessage(message);
+        }
 
-            StatusMessage(long timestamp, String message) {
-                this.timestamp = timestamp;
-                this.message = message;
+        private void addMessage(final String message) {
+            if (storeRealm.isClosed()) return;
+
+            storeRealm.executeTransaction(
+                    new Realm.Transaction() {
+                        @Override
+                        public void execute(Realm realm) {
+                            realm.createObject(StatusStore.class).StatusMessage(message);
+                        }
+                    });
+            if (viewPositionSecondPage < FIRSTPAGE_SIZE) viewPositionSecondPage++; // older session log begins on next page
+            if (viewPosition > 0) viewPosition++; // move the view pointer when not on first page to keep aligned
+            showLog();
+
+            if (viewPosition == 0) {
+                // auto scroll status log
+                if ((mScrollView.getChildAt(0).getBottom() < mScrollView.getHeight()) || ((mScrollView.getChildAt(0).getBottom() - mScrollView.getScrollY() - mScrollView.getHeight()) < (mScrollView.getHeight() / 3))) {
+                    mScrollView.post(new Runnable() {
+                        public void run() {
+                            mScrollView.fullScroll(View.FOCUS_DOWN);
+                        }
+                    });
+                }
             }
 
-            public String toString() {
-                return DateFormat.getTimeInstance(DateFormat.MEDIUM).format(timestamp) + ": " + message;
+            // remove stale items but not while viewing older paged entries
+            if (viewPosition < viewPositionSecondPage) {
+                final RealmResults results = storeRealm.where(StatusStore.class)
+                        .lessThan("timestamp", System.currentTimeMillis() - STALE_MS)
+                        .findAll();
+                if (results.size() > 0) {
+                    storeRealm.executeTransaction(
+                            new Realm.Transaction() {
+                                @Override
+                                public void execute(Realm realm) {
+                                    results.deleteAllFromRealm();
+                                }
+                            });
+                }
             }
         }
 
-        private final Queue<StatusMessage> messages = new ArrayBlockingQueue<>(400);
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String message = intent.getStringExtra(MedtronicCnlIntentService.Constants.EXTENDED_DATA);
-            Log.i(TAG, "Message Receiver: " + message);
+        private void showLog() {
+            RealmResults results = storeRealm.where(StatusStore.class)
+                    .findAllSorted("timestamp", Sort.DESCENDING);
 
-            synchronized (messages) {
-                while (messages.size() > 398) {
-                    messages.poll();
-                }
-                messages.add(new StatusMessage(message));
-            }
+            int remain = results.size() - viewPosition;
+            int segment = remain;
+            if (viewPosition == 0 && viewPositionSecondPage < PAGE_SIZE) segment = viewPositionSecondPage;
+            if (segment > PAGE_SIZE) segment = PAGE_SIZE;
 
             StringBuilder sb = new StringBuilder();
-            for (StatusMessage msg : messages) {
-                if (sb.length() > 0)
-                    sb.append("\n");
-                sb.append(msg);
+            if (segment > 0) {
+                for (int index = viewPosition; index < viewPosition + segment; index++)
+                    sb.insert(0, results.get(index) + (sb.length() > 0 ? "\n" : ""));
             }
-
             mTextViewLog.setText(sb.toString(), BufferType.EDITABLE);
 
-            // auto scroll status log
-            if ((mScrollView.getChildAt(0).getBottom() < mScrollView.getHeight()) || ((mScrollView.getChildAt(0).getBottom() - mScrollView.getScrollY() - mScrollView.getHeight()) < (mScrollView.getHeight() / 3))) {
-                mScrollView.post(new Runnable() {
-                    public void run() {
-                        mScrollView.fullScroll(View.FOCUS_DOWN);
-                    }
-                });
+            if (viewPosition > 0) {
+                mTextViewLogButtonBottom.setVisibility(View.VISIBLE);
+                mTextViewLogButtonBottomRecent.setVisibility(View.VISIBLE);
+            } else {
+                mTextViewLogButtonBottom.setVisibility(View.GONE);
+                mTextViewLogButtonBottomRecent.setVisibility(View.GONE);
+            }
+            if (remain > segment) {
+                mTextViewLogButtonTop.setVisibility(View.VISIBLE);
+            } else {
+                mTextViewLogButtonTop.setVisibility(View.GONE);
+            }
+            if (viewPosition > 0 || mScrollView.getChildAt(0).getBottom() > mScrollView.getHeight() + 100) {
+                mTextViewLogButtonTopRecent.setVisibility(View.VISIBLE);
+            } else {
+                mTextViewLogButtonTopRecent.setVisibility(View.GONE);
             }
         }
 
-        public void clearMessages() {
-            synchronized (messages) {
-                messages.clear();
+        private void clearMessages() {
+            final RealmResults results = storeRealm.where(StatusStore.class)
+                    .findAll();
+            if (results.size() > 0) {
+                storeRealm.executeTransaction(
+                        new Realm.Transaction() {
+                            @Override
+                            public void execute(Realm realm) {
+                                results.deleteAllFromRealm();
+                            }
+                        });
             }
-
             mTextViewLog.setText("", BufferType.EDITABLE);
+            mTextViewLogButtonTop.setVisibility(View.GONE);
+            mTextViewLogButtonTopRecent.setVisibility(View.GONE);
+            mTextViewLogButtonBottom.setVisibility(View.GONE);
+            mTextViewLogButtonBottomRecent.setVisibility(View.GONE);
+            viewPosition = 0;
+            viewPositionSecondPage = 0;
+        }
+
+        private void changeStatusViewOlder() {
+            if (viewPosition == 0 && viewPositionSecondPage < PAGE_SIZE) {
+                viewPosition = viewPositionSecondPage;
+            } else {
+                viewPosition += PAGE_SIZE;
+            }
+            showLog();
+            mScrollView.post(new Runnable() {
+                public void run() {
+                    mScrollView.fullScroll(View.FOCUS_DOWN);
+                    if (viewPosition > 0 || mScrollView.getChildAt(0).getBottom() > mScrollView.getHeight() + 100) {
+                        mTextViewLogButtonTopRecent.setVisibility(View.VISIBLE);
+                    } else {
+                        mTextViewLogButtonTopRecent.setVisibility(View.GONE);
+                    }
+                }
+            });
         }
+        private void changeStatusViewNewer() {
+            viewPosition -= PAGE_SIZE;
+            if (viewPosition < 0) viewPosition = 0;
+            showLog();
+            mScrollView.post(new Runnable() {
+                public void run() {
+                    mScrollView.fullScroll(View.FOCUS_UP);
+                    if (viewPosition > 0 || mScrollView.getChildAt(0).getBottom() > mScrollView.getHeight() + 100) {
+                        mTextViewLogButtonTopRecent.setVisibility(View.VISIBLE);
+                    } else {
+                        mTextViewLogButtonTopRecent.setVisibility(View.GONE);
+                    }
+                }
+            });
+        }
+        private void changeStatusViewRecent() {
+            viewPosition = 0;
+            showLog();
+            mScrollView.post(new Runnable() {
+                public void run() {
+                    mScrollView.fullScroll(View.FOCUS_DOWN);
+                    if (viewPosition > 0 || mScrollView.getChildAt(0).getBottom() > mScrollView.getHeight() + 100) {
+                        mTextViewLogButtonTopRecent.setVisibility(View.VISIBLE);
+                    } else {
+                        mTextViewLogButtonTopRecent.setVisibility(View.GONE);
+                    }
+                }
+            });
+        }
+
     }
 
     private class RefreshDisplayRunnable implements Runnable {
@@ -811,52 +845,96 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
         public void run() {
             long nextRun = 60000L;
 
-            TextView textViewBg = (TextView) findViewById(R.id.textview_bg);
-            TextView textViewBgTime = (TextView) findViewById(R.id.textview_bg_time);
-            TextView textViewUnits = (TextView) findViewById(R.id.textview_units);
-            if (configurationStore.isMmolxl()) {
-                textViewUnits.setText(R.string.text_unit_mmolxl);
-            } else {
-                textViewUnits.setText(R.string.text_unit_mgxdl);
-            }
-            TextView textViewTrend = (TextView) findViewById(R.id.textview_trend);
-            TextView textViewIOB = (TextView) findViewById(R.id.textview_iob);
-
-            // Get the most recently written CGM record for the active pump.
-            PumpStatusEvent pumpStatusData = null;
-
-            if (dataStore.getLastPumpStatus().getEventDate().getTime() > 0) {
-                pumpStatusData = dataStore.getLastPumpStatus();
-            }
-
-            updateChart(mRealm.where(PumpStatusEvent.class)
-                    .notEqualTo("sgv", 0)
-                    .greaterThan("sgvDate", new Date(System.currentTimeMillis() - 1000 * 60 * 60 * 24))
-                    .findAllSorted("sgvDate", Sort.ASCENDING));
+            if (!mRealm.isClosed()) {
 
-            if (pumpStatusData != null) {
-                String sgvString;
-                if (pumpStatusData.isCgmActive()) {
-                    sgvString = MainActivity.strFormatSGV(pumpStatusData.getSgv());
+                TextView textViewBg = (TextView) findViewById(R.id.textview_bg);
+                TextView textViewBgTime = (TextView) findViewById(R.id.textview_bg_time);
+                TextView textViewUnits = (TextView) findViewById(R.id.textview_units);
+                if (configurationStore.isMmolxl()) {
+                    textViewUnits.setText(R.string.text_unit_mmolxl);
+                } else {
+                    textViewUnits.setText(R.string.text_unit_mgxdl);
+                }
+                TextView textViewTrend = (TextView) findViewById(R.id.textview_trend);
+                TextView textViewIOB = (TextView) findViewById(R.id.textview_iob);
+
+                String timeString = "never";
+                String sgvString = "\u2014"; // &mdash;
+                String trendString = "{ion_ios_minus_empty}";
+                int trendRotation = 0;
+                float iob = 0;
+                int battery = 0;
+
+                // most recent sgv status
+                RealmResults<PumpStatusEvent> sgv_results =
+                        mRealm.where(PumpStatusEvent.class)
+                                .equalTo("validSGV", true)
+                                .findAllSorted("cgmDate", Sort.ASCENDING);
+
+                if (sgv_results.size() > 0) {
+                    long sgvtime = sgv_results.last().getCgmDate().getTime();
+                    nextRun = 60000L - (System.currentTimeMillis() - sgvtime) % 60000L;
+                    timeString = (DateUtils.getRelativeTimeSpanString(sgvtime)).toString();
+                    sgvString = MainActivity.strFormatSGV(sgv_results.last().getSgv());
                     if (configurationStore.isMmolxl()) {
                         Log.d(TAG, sgvString + " mmol/L");
                     } else {
                         Log.d(TAG, sgvString + " mg/dL");
                     }
-                } else {
-                    sgvString = "\u2014"; // &mdash;
+
+                    switch (sgv_results.last().getCgmTrend()) {
+                        case DOUBLE_UP:
+                            trendString = "{ion_ios_arrow_thin_up}{ion_ios_arrow_thin_up}";
+                            break;
+                        case SINGLE_UP:
+                            trendString = "{ion_ios_arrow_thin_up}";
+                            break;
+                        case FOURTY_FIVE_UP:
+                            trendRotation = -45;
+                            trendString = "{ion_ios_arrow_thin_right}";
+                            break;
+                        case FLAT:
+                            trendString = "{ion_ios_arrow_thin_right}";
+                            break;
+                        case FOURTY_FIVE_DOWN:
+                            trendRotation = 45;
+                            trendString = "{ion_ios_arrow_thin_right}";
+                            break;
+                        case SINGLE_DOWN:
+                            trendString = "{ion_ios_arrow_thin_down}";
+                            break;
+                        case DOUBLE_DOWN:
+                            trendString = "{ion_ios_arrow_thin_down}{ion_ios_arrow_thin_down}";
+                            break;
+                        default:
+                            trendString = "{ion_ios_minus_empty}";
+                            break;
+                    }
+
+                    updateChart(sgv_results.where()
+                            .greaterThan("cgmDate",  new Date(sgvtime - 1000 * 60 * 60 * 24))
+                            .findAllSorted("cgmDate", Sort.ASCENDING));
                 }
 
-                nextRun = 60000L - (System.currentTimeMillis() - pumpStatusData.getSgvDate().getTime()) % 60000L;
-                textViewBg.setText(sgvString);
-                textViewBgTime.setText(DateUtils.getRelativeTimeSpanString(pumpStatusData.getSgvDate().getTime()));
+                // most recent pump status
+                RealmResults<PumpStatusEvent> pump_results =
+                        mRealm.where(PumpStatusEvent.class)
+                                .findAllSorted("eventDate", Sort.ASCENDING);
 
-                textViewTrend.setText(MainActivity.renderTrendSymbol(pumpStatusData.getCgmTrend()));
-                textViewIOB.setText(String.format(Locale.getDefault(), "%.2f", pumpStatusData.getActiveInsulin()));
+                if (pump_results.size() > 0) {
+                    iob = pump_results.last().getActiveInsulin();
+                    battery = pump_results.last().getBatteryPercentage();
+                }
+
+                textViewBg.setText(sgvString);
+                textViewBgTime.setText(timeString);
+                textViewIOB.setText(String.format(Locale.getDefault(), "%.2f", iob));
+                textViewTrend.setText(trendString);
+                textViewTrend.setRotation(trendRotation);
 
                 ActionMenuItemView batIcon = ((ActionMenuItemView) findViewById(R.id.status_battery));
                 if (batIcon != null) {
-                    switch (pumpStatusData.getBatteryPercentage()) {
+                    switch (battery) {
                         case 0:
                             batIcon.setTitle("0%");
                             batIcon.setIcon(getResources().getDrawable(R.drawable.battery_0));
@@ -882,7 +960,6 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                             batIcon.setIcon(getResources().getDrawable(R.drawable.battery_unknown));
                     }
                 }
-
             }
 
             // Run myself again in 60 (or less) seconds;
@@ -915,7 +992,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
             int pos = 0;
             for (PumpStatusEvent pumpStatus : results) {
                 // turn your data into Entry objects
-                entries[pos++] = new DataPoint(pumpStatus.getSgvDate(), (double) pumpStatus.getSgv());
+                entries[pos++] = new DataPoint(pumpStatus.getCgmDate(), (double) pumpStatus.getSgv());
             }
 
             if (mChart.getSeries().size() == 0) {
@@ -927,12 +1004,9 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
 //                }
 //                entries = Arrays.copyOfRange(entries, 0, j);
 
-                PointsGraphSeries sgvSerie = new PointsGraphSeries(entries);
-//                sgvSerie.setSize(3.6f);
-//                sgvSerie.setColor(Color.LTGRAY);
+                PointsGraphSeries sgvSeries = new PointsGraphSeries(entries);
 
-
-                sgvSerie.setOnDataPointTapListener(new OnDataPointTapListener() {
+                sgvSeries.setOnDataPointTapListener(new OnDataPointTapListener() {
                     DateFormat mFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM);
 
                     @Override
@@ -945,7 +1019,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                     }
                 });
 
-                sgvSerie.setCustomShape(new PointsGraphSeries.CustomShape() {
+                sgvSeries.setCustomShape(new PointsGraphSeries.CustomShape() {
                     @Override
                     public void draw(Canvas canvas, Paint paint, float x, float y, DataPointInterface dataPoint) {
                         double sgv = dataPoint.getY();
@@ -967,7 +1041,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                 });
 
                 mChart.getViewport().setYAxisBoundsManual(false);
-                mChart.addSeries(sgvSerie);
+                mChart.addSeries(sgvSeries);
             } else {
                 if (entries.length > 0) {
                     ((PointsGraphSeries) mChart.getSeries().get(0)).resetData(entries);
@@ -1002,16 +1076,9 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
      * has to be done in MainActivity thread
      */
     private class UpdatePumpReceiver extends BroadcastReceiver {
-
         @Override
         public void onReceive(Context context, Intent intent) {
-            // 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;
-            }
-            //init local pump listener
-            getActivePump();
+            refreshDisplay(500);
         }
     }
 
@@ -1026,6 +1093,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                 boolean permissionGranted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
                 if (permissionGranted) {
                     Log.d(TAG, "Got permission to access USB");
+                    sendStatus(MedtronicCnlIntentService.ICON_INFO + "Got permission to access USB.");
                     startCgmService();
                 } else {
                     Log.d(TAG, "Still no permission for USB. Waiting...");
@@ -1043,6 +1111,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                     startCgmServiceDelayed(MedtronicCnlIntentService.USB_WARMUP_TIME_MS);
                 } else {
                     Log.d(TAG, "No permission for USB. Waiting.");
+                    sendStatus(MedtronicCnlIntentService.ICON_INFO + "Waiting for USB permission.");
                     waitForUsbPermission();
                 }
             } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
@@ -1053,6 +1122,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc
                 }
             } else if (MedtronicCnlIntentService.Constants.ACTION_NO_USB_PERMISSION.equals(action)) {
                 Log.d(TAG, "No permission to read the USB device.");
+                sendStatus(MedtronicCnlIntentService.ICON_INFO + "Requesting USB permission.");
                 requestUsbPermission();
             } else if (MedtronicCnlIntentService.Constants.ACTION_USB_REGISTER.equals(action)) {
                 openUsbRegistration();
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 5778359c0995759e19d9856d5cc20a020a40f775..39f5e1413ed2d29f0a887a1c10d3dc7ca4cc4506 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
@@ -13,7 +13,6 @@ import info.nightscout.android.medtronic.exception.ChecksumException;
 import info.nightscout.android.medtronic.exception.EncryptionException;
 import info.nightscout.android.medtronic.exception.UnexpectedMessageException;
 import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
-import info.nightscout.android.utils.DataStore;
 import info.nightscout.android.utils.HexDump;
 
 /**
@@ -23,15 +22,24 @@ public class PumpStatusResponseMessage extends MedtronicSendMessageResponseMessa
     private static final String TAG = PumpStatusResponseMessage.class.getSimpleName();
 
     // Data from the Medtronic Pump Status message
+
+    private byte pumpStatus;
+    private byte cgmStatus;
     private boolean suspended;
-    private boolean bolusing;
+    private boolean bolusingNormal;
+    private boolean bolusingSquare;
+    private boolean bolusingDual;
     private boolean deliveringInsulin;
     private boolean tempBasalActive;
     private boolean cgmActive;
+    private boolean cgmCalibrating;
+    private boolean cgmCalibrationComplete;
+    private boolean cgmException;
+    private boolean cgmWarmUp;
     private byte activeBasalPattern;
     private float basalRate;
     private float tempBasalRate;
-    private byte tempBasalPercentage;
+    private short tempBasalPercentage;
     private short tempBasalMinutesRemaining;
     private float basalUnitsDeliveredToday;
     private short batteryPercentage;
@@ -39,12 +47,24 @@ public class PumpStatusResponseMessage extends MedtronicSendMessageResponseMessa
     private short minutesOfInsulinRemaining; // 25h == "more than 1 day"
     private float activeInsulin;
     private int sgv;
-    private Date sgvDate;
+    private Date cgmDate;
+    private byte cgmExceptionType;
     private boolean lowSuspendActive;
     private PumpStatusEvent.CGM_TREND 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.
+    private int recentBGL; // in mg/dL. 0 means no recent finger bg reading.
+    private short alert;
+    private Date alertDate;
+    private float bolusingDelivered;
+    private short bolusingMinutesRemaining;
+    private short bolusingReference;
+    private float lastBolusAmount;
+    private Date lastBolusDate;
+    private short lastBolusReference;
+    private byte transmitterBattery;
+    private byte transmitterControl;
+    private short calibrationDueMinutes;
+    private float sensorRateOfChange;
 
     protected PumpStatusResponseMessage(MedtronicCnlSession pumpSession, byte[] payload) throws EncryptionException, ChecksumException, UnexpectedMessageException {
         super(pumpSession, payload);
@@ -65,77 +85,119 @@ public class PumpStatusResponseMessage extends MedtronicSendMessageResponseMessa
             String outputString = HexDump.dumpHexString(statusBuffer.array());
             Log.d(TAG, "PAYLOAD: " + outputString);
         }
+
         // Status Flags
-        suspended = (statusBuffer.get(0x03) & 0x01) != 0x00;
-        bolusing = (statusBuffer.get(0x03) & 0x02) != 0x00;
-        deliveringInsulin = (statusBuffer.get(0x03) & 0x10) != 0x00;
-        tempBasalActive = (statusBuffer.get(0x03) & 0x20) != 0x00;
-        cgmActive = (statusBuffer.get(0x03) & 0x40) != 0x00;
+        pumpStatus = statusBuffer.get(0x03);
+        cgmStatus = statusBuffer.get(0x41);
+
+        suspended = (pumpStatus & 0x01) != 0x00;
+        bolusingNormal = (pumpStatus & 0x02) != 0x00;
+        bolusingSquare = (pumpStatus & 0x04) != 0x00;
+        bolusingDual = (pumpStatus & 0x08) != 0x00;
+        deliveringInsulin = (pumpStatus & 0x10) != 0x00;
+        tempBasalActive = (pumpStatus & 0x20) != 0x00;
+        cgmActive = (pumpStatus & 0x40) != 0x00;
+        cgmCalibrating = (cgmStatus & 0x01) != 0x00;
+        cgmCalibrationComplete = (cgmStatus & 0x02) != 0x00;
+        cgmException = (cgmStatus & 0x04) != 0x00;
 
         // Active basal pattern
-        activeBasalPattern = statusBuffer.get(0x1a);
+        activeBasalPattern = statusBuffer.get(0x1A);
 
         // Normal basal rate
-        long rawNormalBasal = statusBuffer.getInt(0x1b);
+        long rawNormalBasal = statusBuffer.getInt(0x1B);
         basalRate = new BigDecimal(rawNormalBasal / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue();
 
         // Temp basal rate
-        long rawTempBasal = statusBuffer.getShort(0x21) & 0x0000ffff;
+        long rawTempBasal = statusBuffer.getInt(0x1F) & 0x0000000000FFFFFFL;
         tempBasalRate = new BigDecimal(rawTempBasal / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue();
 
         // Temp basal percentage
-        tempBasalPercentage = statusBuffer.get(0x23);
+        tempBasalPercentage =  (short) (statusBuffer.get(0x23) & 0x00FF);
 
         // Temp basal minutes remaining
-        tempBasalMinutesRemaining = (short) (statusBuffer.getShort(0x24) & 0x0000ffff);
+        tempBasalMinutesRemaining = (short) (statusBuffer.getShort(0x24) & 0x00FF);
 
         // Units of insulin delivered as basal today
-        // TODO - is this basal? Do we have a total Units delivered elsewhere?
-        basalUnitsDeliveredToday = statusBuffer.getInt(0x26);
+        long rawBasalUnitsDeliveredToday = statusBuffer.getInt(0x26) & 0x0000000000FFFFFFL;
+        basalUnitsDeliveredToday = new BigDecimal(rawBasalUnitsDeliveredToday / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue();
 
         // Pump battery percentage
-        batteryPercentage = statusBuffer.get(0x2a);
+        batteryPercentage = statusBuffer.get(0x2A);
 
         // Reservoir amount
-        long rawReservoirAmount = statusBuffer.getInt(0x2b);
+        long rawReservoirAmount = statusBuffer.getInt(0x2B) & 0x0000000000FFFFFFL;
         reservoirAmount = 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 insulinHours = statusBuffer.get(0x2F);
         byte insulinMinutes = statusBuffer.get(0x30);
         minutesOfInsulinRemaining = (short) ((insulinHours * 60) + insulinMinutes);
 
         // Active insulin
-        long rawActiveInsulin = statusBuffer.getInt(0x31);
+        long rawActiveInsulin = statusBuffer.getInt(0x31) & 0x0000000000FFFFFFL;
         activeInsulin = new BigDecimal(rawActiveInsulin / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue();
 
         // CGM SGV
-        sgv = (statusBuffer.getShort(0x35) & 0x0000ffff); // In mg/DL. 0 means no CGM reading
-        long rtc;
-        long offset;
-        if ((sgv & 0x200) == 0x200) {
-            // Sensor error. Let's reset. FIXME - solve this more elegantly later
-            sgv = 0;
-            rtc = 0;
-            offset = 0;
+        sgv = (statusBuffer.getShort(0x35) & 0x0000FFFF); // In mg/DL. 0x0000 = no CGM reading, 0x03NN = sensor exception
+        cgmDate = MessageUtils.decodeDateTime((long) statusBuffer.getInt(0x37) & 0x00000000FFFFFFFFL, (long) statusBuffer.getInt(0x3B));
+        Log.d(TAG, "original cgm/sgv date: " + cgmDate);
+
+        if (cgmException) {
+            cgmExceptionType = (byte) (sgv & 0x00FF);
             cgmTrend = PumpStatusEvent.CGM_TREND.NOT_SET;
+            if (cgmExceptionType == 0x01) cgmWarmUp = true;
+            sgv = 0;
         } else {
-            rtc = statusBuffer.getInt(0x37) & 0x00000000ffffffffL;
-            offset = statusBuffer.getInt(0x3b);
-            cgmTrend = PumpStatusEvent.CGM_TREND.fromMessageByte(statusBuffer.get(0x40));
+            cgmExceptionType = 0;
+            cgmTrend = PumpStatusEvent.CGM_TREND.fromMessageByte((byte) (statusBuffer.get(0x40) & 0xF0)); // masked as low nibble can contain value when transmitter battery low
+            cgmWarmUp = false;
         }
 
-        // SGV Date
-        sgvDate = MessageUtils.decodeDateTime(rtc, offset);
-        Log.d(TAG, "original sgv date: " + sgvDate);
-
         // Predictive low suspend
         // TODO - there is more status info in this byte other than just a boolean yes/no
-        lowSuspendActive = statusBuffer.get(0x3f) != 0;
+        // noted: 0x01=high 0x04=before high 0x08=before low 0x0A=low 0x80=suspend 0x92=suspend low
+        lowSuspendActive = statusBuffer.get(0x3F) != 0;
 
-        // Recent Bolus Wizard BGL
+        // Recent Bolus Wizard
         recentBolusWizard = statusBuffer.get(0x48) != 0;
-        bolusWizardBGL = statusBuffer.getShort(0x49) & 0x0000ffff; // In mg/DL
+
+        // Recent BGL
+        recentBGL = statusBuffer.getShort(0x49) & 0x0000FFFF; // In mg/DL
+
+        // Active alert
+        alert = statusBuffer.getShort(0x4B);
+        alertDate = MessageUtils.decodeDateTime((long) statusBuffer.getInt(0x4D) & 0x00000000FFFFFFFFL, (long) statusBuffer.getInt(0x51));
+
+        // Now bolusing
+        long rawBolusingDelivered = statusBuffer.getInt(0x04) & 0x0000000000FFFFFFL;
+        bolusingDelivered = new BigDecimal(rawBolusingDelivered / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue();
+        bolusingMinutesRemaining = statusBuffer.getShort(0x0C);
+        bolusingReference = statusBuffer.getShort(0x0E);
+
+        // Last bolus
+        long rawLastBolusAmount = statusBuffer.getInt(0x10) & 0x0000000000FFFFFFL;
+        lastBolusAmount = new BigDecimal(rawLastBolusAmount / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue();
+        lastBolusDate = MessageUtils.decodeDateTime((long) statusBuffer.getInt(0x14) & 0x00000000FFFFFFFFL, 0);
+        lastBolusReference = statusBuffer.getShort(0x18);
+
+        // Calibration
+        calibrationDueMinutes = statusBuffer.getShort(0x43);
+
+        // Transmitter
+        transmitterControl = statusBuffer.get(0x43);
+        transmitterBattery  = statusBuffer.get(0x45);
+        // Normalise transmitter battery to percentage shown on pump sensor status screen
+        if (transmitterBattery == 0x3F) transmitterBattery = 100;
+        else if (transmitterBattery == 0x2B) transmitterBattery = 73;
+        else if (transmitterBattery == 0x27) transmitterBattery = 47;
+        else if (transmitterBattery == 0x23) transmitterBattery = 20;
+        else if (transmitterBattery == 0x10) transmitterBattery = 0;
+        else transmitterBattery = 0;
+
+        // Sensor
+        long rawSensorRateOfChange = statusBuffer.getShort(0x46);
+        sensorRateOfChange = new BigDecimal(rawSensorRateOfChange / 100f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue();
     }
 
     /**
@@ -144,13 +206,24 @@ public class PumpStatusResponseMessage extends MedtronicSendMessageResponseMessa
      * @param pumpRecord
      */
     public void updatePumpRecord(PumpStatusEvent pumpRecord) {
+
         // Status Flags
+        pumpRecord.setPumpStatus(pumpStatus);
+        pumpRecord.setCgmStatus(cgmStatus);
+
         pumpRecord.setSuspended(suspended);
-        pumpRecord.setBolusing(bolusing);
+        pumpRecord.setBolusingNormal(bolusingNormal);
+        pumpRecord.setBolusingSquare(bolusingSquare);
+        pumpRecord.setBolusingDual(bolusingDual);
         pumpRecord.setDeliveringInsulin(deliveringInsulin);
         pumpRecord.setTempBasalActive(tempBasalActive);
         pumpRecord.setCgmActive(cgmActive);
 
+        pumpRecord.setCgmCalibrating(cgmCalibrating);
+        pumpRecord.setCgmCalibrationComplete(cgmCalibrationComplete);
+        pumpRecord.setCgmException(cgmException);
+        pumpRecord.setCgmWarmUp(cgmWarmUp);
+
         // Active basal pattern
         pumpRecord.setActiveBasalPattern(activeBasalPattern);
 
@@ -183,26 +256,42 @@ public class PumpStatusResponseMessage extends MedtronicSendMessageResponseMessa
 
         // CGM SGV data
         pumpRecord.setSgv(sgv);
-        pumpRecord.setSgvDate(new Date(sgvDate.getTime() - pumpRecord.getPumpTimeOffset()));
+        pumpRecord.setCgmPumpDate(cgmDate);
+        pumpRecord.setCgmDate(new Date(cgmDate.getTime() - pumpRecord.getPumpTimeOffset()));
         pumpRecord.setCgmTrend(cgmTrend);
+        pumpRecord.setCgmExceptionType(cgmExceptionType);
 
         // Predictive low suspend
         // TODO - there is more status info in this byte other than just a boolean yes/no
         pumpRecord.setLowSuspendActive(lowSuspendActive);
 
-        // Recent Bolus Wizard BGL
-        if (bolusWizardBGL > 0
-                && (DataStore.getInstance().getLastPumpStatus().getSgvDate().getTime() - System.currentTimeMillis() > 15 * 60 * 1000
-                || (DataStore.getInstance().getLastBolusWizardBGL() != bolusWizardBGL
-                && DataStore.getInstance().getLastPumpStatus().getBolusWizardBGL() != bolusWizardBGL)
-                )
-        ) {
-            pumpRecord.setRecentBolusWizard(true);
-            pumpRecord.setBolusWizardBGL(bolusWizardBGL); // In mg/DL
-        } else {
-            pumpRecord.setRecentBolusWizard(false);
-            pumpRecord.setBolusWizardBGL(0); // In mg/DL
-        }
-        DataStore.getInstance().setLastBolusWizardBGL(bolusWizardBGL);
+        // Recent BGL
+        pumpRecord.setRecentBGL(recentBGL); // In mg/DL
+
+        // Active alert
+        pumpRecord.setAlert(alert);
+        pumpRecord.setAlertPumpDate(alertDate);
+        pumpRecord.setAlertDate(new Date(alertDate.getTime() - pumpRecord.getPumpTimeOffset()));
+
+        // Now bolusing
+        pumpRecord.setBolusingDelivered(bolusingDelivered);
+        pumpRecord.setBolusingMinutesRemaining(bolusingMinutesRemaining);
+        pumpRecord.setBolusingReference(bolusingReference);
+
+        // Last bolus
+        pumpRecord.setLastBolusAmount(lastBolusAmount);
+        pumpRecord.setLastBolusPumpDate(lastBolusDate);
+        pumpRecord.setLastBolusDate(new Date(lastBolusDate.getTime() - pumpRecord.getPumpTimeOffset()));
+        pumpRecord.setLastBolusReference(lastBolusReference);
+
+        // Calibration
+        pumpRecord.setCalibrationDueMinutes(calibrationDueMinutes);
+
+        // Transmitter
+        pumpRecord.setTransmitterBattery(transmitterBattery);
+        pumpRecord.setTransmitterControl(transmitterControl);
+
+        // Sensor
+        pumpRecord.setSensorRateOfChange(sensorRateOfChange);
     }
-}
+}
\ No newline at end of file
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 bf3c333c40b8b990778f18afff76b49638c68651..d1a80edadf8b029d8aedefa861d07879285c2c9a 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
@@ -40,33 +40,49 @@ import info.nightscout.android.utils.DataStore;
 import info.nightscout.android.xdrip_plus.XDripPlusUploadReceiver;
 import io.realm.Realm;
 import io.realm.RealmResults;
+import io.realm.Sort;
 
 public class MedtronicCnlIntentService extends IntentService {
     public final static int USB_VID = 0x1a79;
     public final static int USB_PID = 0x6210;
     public final static long USB_WARMUP_TIME_MS = 5000L;
+
     public final static long POLL_PERIOD_MS = 300000L;
     public final static long LOW_BATTERY_POLL_PERIOD_MS = 900000L;
     // Number of additional seconds to wait after the next expected CGM poll, so that we don't interfere with CGM radio comms.
     public final static long POLL_GRACE_PERIOD_MS = 30000L;
+    // Number of seconds before the next expected CGM poll that we will allow uploader comms to start
     public final static long POLL_PRE_GRACE_PERIOD_MS = 45000L;
-
-    public static final String ICON_WARN = "{ion-alert-circled} ";
-    public static final String ICON_BGL = "{ion-waterdrop} ";
-    public static final String ICON_USB = "{ion-usb} ";
-    public static final String ICON_INFO = "{ion-information_circled} ";
-    public static final String ICON_HELP = "{ion-ios-lightbulb} ";
-    public static final String ICON_SETTING = "{ion-android-settings} ";
-    public static final String ICON_HEART = "{ion-heart} ";
-    public static final String ICON_STAR = "{ion-ios-star} ";
+    // cgm n/a events to trigger anti clash poll timing
+    public final static int POLL_ANTI_CLASH = 3;
+
+    // TODO - use a message type and insert icon as part of ui status message handling
+    public static final String ICON_WARN = "{ion_alert_circled} ";
+    public static final String ICON_BGL = "{ion_waterdrop} ";
+    public static final String ICON_USB = "{ion_usb} ";
+    public static final String ICON_INFO = "{ion_information_circled} ";
+    public static final String ICON_HELP = "{ion_ios_lightbulb} ";
+    public static final String ICON_SETTING = "{ion_android_settings} ";
+    public static final String ICON_HEART = "{ion_heart} ";
+    public static final String ICON_LOW = "{ion_battery_low} ";
+    public static final String ICON_FULL = "{ion_battery_full} ";
+    public static final String ICON_CGM = "{ion_ios_pulse_strong} ";
+    public static final String ICON_SUSPEND = "{ion_pause} ";
+    public static final String ICON_RESUME = "{ion_play} ";
+    public static final String ICON_BOLUS = "{ion_skip_forward} ";
+    public static final String ICON_BASAL = "{ion_skip_forward} ";
+    public static final String ICON_CHANGE = "{ion_android_hand} ";
+    public static final String ICON_BELL = "{ion_android_notifications} ";
+    public static final String ICON_NOTE = "{ion_clipboard} ";
 
     // show warning message after repeated errors
     private final static int ERROR_COMMS_AT = 4;
-    private final static int ERROR_CONNECT_AT = 4;
-    private final static int ERROR_SIGNAL_AT = 4;
-    private final static float ERROR_UNAVAILABLE_AT = 12;       // warning at
-    private final static float ERROR_UNAVAILABLE_RATE = 12 / 3; // expected per hour / acceptable unavailable per hour
-    private final static float ERROR_UNAVAILABLE_DECAY = -1;    // decay rate for each good sgv received
+    private final static int ERROR_CONNECT_AT = 6;
+    private final static int ERROR_SIGNAL_AT = 6;
+    private final static int ERROR_PUMPLOSTSENSOR_AT = 6;
+    private final static int ERROR_PUMPBATTERY_AT = 3;
+    private final static int ERROR_PUMPCLOCK_AT = 8;
+    private final static int ERROR_PUMPCLOCK_MS = 10 * 60 * 1000;
 
     private static final String TAG = MedtronicCnlIntentService.class.getSimpleName();
 
@@ -77,7 +93,9 @@ public class MedtronicCnlIntentService extends IntentService {
     private DataStore dataStore = DataStore.getInstance();
     private ConfigurationStore configurationStore = ConfigurationStore.getInstance();
     private DateFormat dateFormatter = new SimpleDateFormat("HH:mm:ss", Locale.US);
-
+    private DateFormat dateFormatterNote = new SimpleDateFormat("E HH:mm", Locale.US);
+    private Realm realm;
+    private long pumpOffset;
 
     public MedtronicCnlIntentService() {
         super(MedtronicCnlIntentService.class.getName());
@@ -143,57 +161,23 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received
     protected void onHandleIntent(Intent intent) {
         Log.d(TAG, "onHandleIntent called");
         try {
-            // TODO use of ConfigurationStore is confusinng if pollInterval uses the CS, which
-            // uses the POLL_PERIOD_MS, while the latter constant is also used directly.
-
-            // Note that the variable pollInterval refers to the poll we'd like to make to the pump,
-            // based on settings and battery level, while POLL_PERIOD_MS is used to calculate
-            // when the pump is going to poll data from the transmitter again.
-            // Thus POLL_PERIOD_MS is important to calculate times we'd be clashing with transmitter
-            // to pump transmissions, which are then checked against the time the uploader would
-            // like to poll, which is calculated using the pollInterval variable.
-            // TODO find better variable names to make this distinction clearer and/or if possible
-            // do more method extraction refactorings to make this method easier to grasp
-
             final long timePollStarted = System.currentTimeMillis();
+            long pollInterval = configurationStore.getPollInterval();
+            realm = Realm.getDefaultInstance();
 
-            long timeLastGoodSGV = dataStore.getLastPumpStatus().getSgvDate().getTime();
-            if (dataStore.getLastPumpStatus().getSgv() == 0
-                || timePollStarted - timeLastGoodSGV > 24 * 60 * 60 * 1000) {
-                timeLastGoodSGV = 0;
-            }
-
-            final long timePollExpected;
-            if (timeLastGoodSGV != 0) {
-                timePollExpected = timeLastGoodSGV + POLL_PERIOD_MS + POLL_GRACE_PERIOD_MS + (POLL_PERIOD_MS * ((timePollStarted - 1000L - (timeLastGoodSGV + POLL_GRACE_PERIOD_MS)) / POLL_PERIOD_MS));
-            } else {
-                timePollExpected = timePollStarted;
-            }
-
-            // avoid polling when too close to sensor-pump comms
-            if (((timePollExpected - timePollStarted) > 5000L) && ((timePollExpected - timePollStarted) < (POLL_PRE_GRACE_PERIOD_MS + POLL_GRACE_PERIOD_MS))) {
-                sendStatus("Please wait: Pump is expecting sensor communication. Poll due in " + ((timePollExpected - timePollStarted) / 1000L) + " seconds");
-                MedtronicCnlAlarmManager.setAlarm(timePollExpected);
+            long due = checkPollTime();
+            if (due > 0) {
+                sendStatus("Please wait: Pump is expecting sensor communication. Poll due in " + ((due - System.currentTimeMillis()) / 1000L) + " seconds");
+                MedtronicCnlAlarmManager.setAlarm(due);
                 return;
             }
 
-            final short pumpBatteryLevel = dataStore.getLastPumpStatus().getBatteryPercentage();
-            long pollInterval = configurationStore.getPollInterval();
-            if ((pumpBatteryLevel > 0) && (pumpBatteryLevel <= 25)) {
-                pollInterval = configurationStore.getLowBatteryPollInterval();
-                sendStatus(ICON_WARN + "Warning: pump battery low");
-                if (pollInterval != configurationStore.getPollInterval()) {
-                    sendStatus(ICON_SETTING + "Low battery poll interval: " + (pollInterval / 60000) +" minutes");
-               }
-            }
-
             // TODO - throw, don't return
             if (!openUsbDevice())
                 return;
 
             MedtronicCnlReader cnlReader = new MedtronicCnlReader(mHidDevice);
 
-            Realm realm = Realm.getDefaultInstance();
             realm.beginTransaction();
 
             try {
@@ -223,7 +207,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received
 
                     // always get LinkKey on startup to handle re-paired CNL-PUMP key changes
                     String key = null;
-                    if (dataStore.getCommsSuccessCount() > 0) {
+                    if (dataStore.getCommsSuccess() > 0) {
                         key = info.getKey();
                     }
 
@@ -253,22 +237,21 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received
                     if (radioChannel == 0) {
                         sendStatus(ICON_WARN + "Could not communicate with the pump. Is it nearby?");
                         Log.i(TAG, "Could not communicate with the pump. Is it nearby?");
-                        dataStore.incCommsConnectThreshold();
-                        pollInterval = configurationStore.getPollInterval() / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L); // reduce polling interval to half until pump is available
+                        dataStore.incCommsConnectError();
+                        pollInterval = POLL_PERIOD_MS / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L); // reduce polling interval to half until pump is available
                     } else if (cnlReader.getPumpSession().getRadioRSSIpercentage() < 5) {
                         sendStatus(String.format(Locale.getDefault(), "Connected on channel %d  RSSI: %d%%", (int) radioChannel, cnlReader.getPumpSession().getRadioRSSIpercentage()));
                         sendStatus(ICON_WARN + "Warning: pump signal too weak. Is it nearby?");
                         Log.i(TAG, "Warning: pump signal too weak. Is it nearby?");
-                        dataStore.incCommsConnectThreshold();
-                        dataStore.incCommsSignalThreshold();
-                        pollInterval = configurationStore.getPollInterval() / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L); // reduce polling interval to half until pump is available
+                        dataStore.incCommsConnectError();
+                        dataStore.incCommsSignalError();
+                        pollInterval = POLL_PERIOD_MS / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L); // reduce polling interval to half until pump is available
                     } else {
-                        dataStore.decCommsConnectThreshold();
-                        if (cnlReader.getPumpSession().getRadioRSSIpercentage() < 20) {
-                            if (dataStore.getCommsSignalThreshold() < ERROR_SIGNAL_AT) dataStore.incCommsSignalThreshold();
-                        } else {
-                            dataStore.decCommsSignalThreshold();
-                        }
+                        dataStore.decCommsConnectError();
+                        if (cnlReader.getPumpSession().getRadioRSSIpercentage() < 20)
+                            dataStore.incCommsSignalError();
+                        else
+                            dataStore.decCommsSignalError();
 
                         dataStore.setActivePumpMac(pumpMAC);
 
@@ -285,79 +268,85 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received
                         // TODO - this should not be necessary. We should reverse lookup the device name from PumpInfo
                         pumpRecord.setDeviceName(deviceName);
 
+                        if (radioChannel == 26) cnlReader.beginEHSMSession(); // gentle persuasion to leave channel 26 (weakest for CNL and causes Pebble to lose BT often) by using EHSM to influence pump channel change
+
                         long pumpTime = cnlReader.getPumpTime().getTime();
-                        long pumpOffset = pumpTime - System.currentTimeMillis();
+                        pumpOffset = pumpTime - System.currentTimeMillis();
                         Log.d(TAG, "Time offset between pump and device: " + pumpOffset + " millis.");
 
-                        if (Math.abs(pumpOffset) > 10 * 60 * 1000) {
-                            sendStatus(ICON_WARN + "Warning: Time difference between Pump and Uploader excessive."
-                                    + " Pump is over " + (Math.abs(pumpOffset) / 60000L) + " minutes " + (pumpOffset > 0 ? "ahead" : "behind") + " of time used by uploader.");
-                            sendStatus(ICON_HELP + "The uploader phone/device should have the current time provided by network. Pump clock drifts forward and needs to be set to correct time occasionally.");
-                        }
-
-                        // TODO - send ACTION to MainActivity to show offset between pump and uploader.
                         pumpRecord.setPumpTimeOffset(pumpOffset);
-                        pumpRecord.setPumpDate(new Date(pumpTime - pumpOffset));
+                        pumpRecord.setPumpDate(new Date(pumpTime));
+                        pumpRecord.setEventDate(new Date(System.currentTimeMillis()));
                         cnlReader.updatePumpStatus(pumpRecord);
 
-                        if (pumpRecord.getSgv() != 0) {
-                            sendStatus("SGV: " + MainActivity.strFormatSGV(pumpRecord.getSgv())
-                                + "  At: " + dateFormatter.format(pumpRecord.getSgvDate().getTime())
-                                + "  Pump: " + (pumpOffset > 0 ? "+" : "") + (pumpOffset / 1000L) + "sec");
-                            // Check if pump sent old event when new expected
-                            if (dataStore.getLastPumpStatus() != null &&
-                                    dataStore.getLastPumpStatus().getSgvDate() != null &&
-                                    pumpRecord.getSgvDate().getTime() - dataStore.getLastPumpStatus().getSgvDate().getTime() < 5000L &&
-                                    timePollExpected - timePollStarted < 5000L) {
-                                sendStatus(ICON_WARN + "Pump sent old SGV event");
-                                if (dataStore.getCommsUnavailableThreshold() < ERROR_UNAVAILABLE_AT) dataStore.addCommsUnavailableThreshold(ERROR_UNAVAILABLE_RATE / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L));
-                                // pump may have missed sensor transmission or be delayed in posting to status message
-                                // in most cases the next scheduled poll will have latest sgv, occasionally it is available this period after a delay
-                                // if user selects double poll option we try again this period or wait until next
-                                pollInterval = POLL_PERIOD_MS / (configurationStore.isReducePollOnPumpAway() ? 2L : 1L);
-                            } else {
-                                dataStore.addCommsUnavailableThreshold(ERROR_UNAVAILABLE_DECAY);
-                            }
+                        if (radioChannel == 26) cnlReader.endEHSMSession();
+
+                        validatePumpRecord(pumpRecord, activePump);
+                        activePump.getPumpHistory().add(pumpRecord);
+                        realm.commitTransaction();
+
+                        dataStore.incCommsSuccess();
+                        dataStore.clearCommsError();
+
+                        if (pumpRecord.getBatteryPercentage() <= 25) {
+                            dataStore.incPumpBatteryError();
+                            pollInterval = configurationStore.getLowBatteryPollInterval();
+                        } else {
+                            dataStore.clearPumpBatteryError();
+                        }
 
-                            dataStore.clearUnavailableSGVCount(); // reset unavailable sgv count
-
-                            // Check that the record doesn't already exist before committing
-                            RealmResults<PumpStatusEvent> checkExistingRecords = activePump.getPumpHistory()
-                                    .where()
-                                    .equalTo("sgvDate", pumpRecord.getSgvDate())
-                                    .equalTo("sgv", pumpRecord.getSgv())
-                                    .findAll();
-
-                            // There should be the 1 record we've already added in this transaction.
-                            if (checkExistingRecords.size() == 0) {
-                                timeLastGoodSGV = pumpRecord.getSgvDate().getTime();
-                                activePump.getPumpHistory().add(pumpRecord);
-                                dataStore.setLastPumpStatus(pumpRecord);
-                                if (pumpRecord.getBolusWizardBGL() != 0) {
-                                    sendStatus(ICON_BGL +"Recent finger BG: " + MainActivity.strFormatSGV(pumpRecord.getBolusWizardBGL()));
+                        if (pumpRecord.isCgmActive()) {
+                            dataStore.clearPumpCgmNA(); // poll clash detection
+                            dataStore.clearPumpLostSensorError();
+
+                            if (pumpRecord.isCgmWarmUp())
+                                sendStatus(ICON_CGM + "sensor is in warm-up phase");
+                            else if (pumpRecord.getCalibrationDueMinutes() == 0)
+                                sendStatus(ICON_CGM + "sensor calibration is due now!");
+                            else if (pumpRecord.getSgv() == 0 && pumpRecord.isCgmCalibrating())
+                                sendStatus(ICON_CGM + "sensor is calibrating");
+                            else if (pumpRecord.getSgv() == 0)
+                                sendStatus(ICON_CGM + "sensor error (pump graph gap)");
+                            else {
+                                dataStore.incCommsSgvSuccess();
+                                // TODO - don't convert SGV here, convert dynamically in ui status message handler
+                                sendStatus("SGV: " + MainActivity.strFormatSGV(pumpRecord.getSgv())
+                                        + "  At: " + dateFormatter.format(pumpRecord.getCgmDate().getTime())
+                                        + "  Pump: " + (pumpOffset > 0 ? "+" : "") + (pumpOffset / 1000L) + "sec");
+                                if (pumpRecord.isCgmCalibrating())
+                                    sendStatus(ICON_CGM + "sensor is calibrating");
+                                if (pumpRecord.isOldSgvWhenNewExpected()) {
+                                    sendStatus(ICON_CGM + "old SGV event received");
+                                    // pump may have missed sensor transmission or be delayed in posting to status message
+                                    // in most cases the next scheduled poll will have latest sgv, occasionally it is available this period after a delay
+                                    // if user selects double poll option we try again this period or wait until next
+                                    pollInterval = configurationStore.isReducePollOnPumpAway() ? 60000 : POLL_PERIOD_MS;
                                 }
                             }
 
                         } else {
-                            sendStatus(ICON_WARN + "SGV: unavailable from pump");
-                            dataStore.incUnavailableSGVCount(); // poll clash detection
-                            if (dataStore.getCommsUnavailableThreshold() < ERROR_UNAVAILABLE_AT) dataStore.addCommsUnavailableThreshold(ERROR_UNAVAILABLE_RATE);
+                            dataStore.incPumpCgmNA(); // poll clash detection
+                            if (dataStore.getCommsSgvSuccess() > 0) {
+                                dataStore.incPumpLostSensorError(); // only count errors if cgm is being used
+                                sendStatus(ICON_CGM + "cgm n/a (pump lost sensor)");
+                            } else {
+                                sendStatus(ICON_CGM + "cgm n/a");
+                            }
                         }
 
-                        realm.commitTransaction();
+                        sendStatusNotifications(pumpRecord);
+
                         // Tell the Main Activity we have new data
                         sendMessage(Constants.ACTION_UPDATE_PUMP);
-                        dataStore.incCommsSuccessCount();
-                        dataStore.clearCommsErrorCount();
                     }
 
                 } catch (UnexpectedMessageException e) {
-                    dataStore.incCommsErrorCount();
+                    dataStore.incCommsError();
                     pollInterval = 60000L; // retry once during this poll period, this allows for transient radio noise
                     Log.e(TAG, "Unexpected Message", e);
                     sendStatus(ICON_WARN + "Communication Error: " + e.getMessage());
                 } catch (TimeoutException e) {
-                    dataStore.incCommsErrorCount();
+                    dataStore.incCommsError();
                     pollInterval = 90000L; // retry once during this poll period, this allows for transient radio noise
                     Log.e(TAG, "Timeout communicating with the Contour Next Link.", e);
                     sendStatus(ICON_WARN + "Timeout communicating with the Contour Next Link / Pump.");
@@ -369,102 +358,496 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received
                         cnlReader.closeConnection();
                         cnlReader.endPassthroughMode();
                         cnlReader.endControlMode();
-                    } catch (NoSuchAlgorithmException e) {
-                    }
-
+                    } catch (NoSuchAlgorithmException e) {}
                 }
             } catch (IOException e) {
-                dataStore.incCommsErrorCount();
+                dataStore.incCommsError();
                 Log.e(TAG, "Error connecting to Contour Next Link.", e);
                 sendStatus(ICON_WARN + "Error connecting to Contour Next Link.");
             } catch (ChecksumException e) {
-                dataStore.incCommsErrorCount();
+                dataStore.incCommsError();
                 Log.e(TAG, "Checksum error getting message from the Contour Next Link.", e);
                 sendStatus(ICON_WARN + "Checksum error getting message from the Contour Next Link.");
             } catch (EncryptionException e) {
-                dataStore.incCommsErrorCount();
+                dataStore.incCommsError();
                 Log.e(TAG, "Error decrypting messages from Contour Next Link.", e);
                 sendStatus(ICON_WARN + "Error decrypting messages from Contour Next Link.");
             } catch (TimeoutException e) {
-                dataStore.incCommsErrorCount();
+                dataStore.incCommsError();
                 Log.e(TAG, "Timeout communicating with the Contour Next Link.", e);
                 sendStatus(ICON_WARN + "Timeout communicating with the Contour Next Link.");
             } catch (UnexpectedMessageException e) {
-                dataStore.incCommsErrorCount();
+                dataStore.incCommsError();
                 Log.e(TAG, "Could not close connection.", e);
                 sendStatus(ICON_WARN + "Could not close connection: " + e.getMessage());
             } finally {
-                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();
+                if (realm.isInTransaction()) {
+                    // If we didn't commit the transaction, we've run into an error. Let's roll it back
+                    realm.cancelTransaction();
                 }
 
+                long nextpoll = requestPollTime(timePollStarted, pollInterval);
+                MedtronicCnlAlarmManager.setAlarm(nextpoll);
+                sendStatus("Next poll due at: " + dateFormatter.format(nextpoll));
+
+                RemoveOutdatedRecords();
                 uploadPollResults();
-                scheduleNextPoll(timePollStarted, timeLastGoodSGV, pollInterval);
+                sendStatusWarnings();
+            }
+
+        } finally {
+            if (!realm.isClosed()) realm.close();
+            MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
+        }
+    }
+
+    private void sendStatusNotifications(PumpStatusEvent pumpRecord) {
+        if (pumpRecord.isValidBGL())
+            // TODO - don't convert BGL here, convert dynamically in ui status message handler
+            sendStatus(ICON_BGL + "Recent finger BG: " + MainActivity.strFormatSGV(pumpRecord.getRecentBGL()));
+
+        if (pumpRecord.isValidBolus()) {
+            if (pumpRecord.isValidBolusSquare())
+                sendStatus(ICON_BOLUS + "Square bolus delivered: " + pumpRecord.getLastBolusAmount() + "u Started: " + dateFormatter.format(pumpRecord.getLastBolusDate()) + " Duration: " + pumpRecord.getLastBolusDuration() + " minutes");
+            else if (pumpRecord.isValidBolusDual())
+                sendStatus(ICON_BOLUS + "Bolus (dual normal part): " + pumpRecord.getLastBolusAmount() + "u At: " + dateFormatter.format(pumpRecord.getLastBolusDate()));
+            else
+                sendStatus(ICON_BOLUS + "Bolus: " + pumpRecord.getLastBolusAmount() + "u At: " + dateFormatter.format(pumpRecord.getLastBolusDate()));
+        }
+
+        if (pumpRecord.isValidTEMPBASAL()) {
+            if (pumpRecord.getTempBasalMinutesRemaining() > 0 && pumpRecord.getTempBasalPercentage() > 0)
+                sendStatus(ICON_BASAL + "Temp basal: " + pumpRecord.getTempBasalPercentage() + "% Remaining: " + pumpRecord.getTempBasalMinutesRemaining() + " minutes");
+            else if (pumpRecord.getTempBasalMinutesRemaining() > 0)
+                sendStatus(ICON_BASAL + "Temp basal: " + pumpRecord.getTempBasalRate() + "u Remaining: " + pumpRecord.getTempBasalMinutesRemaining() + " minutes");
+            else
+                sendStatus(ICON_BASAL + "Temp basal: stopped before expected duration");
+        }
+
+        if (pumpRecord.isValidSUSPEND())
+            sendStatus(ICON_SUSPEND + "Pump suspended insulin delivery approx: " + dateFormatterNote.format(pumpRecord.getSuspendAfterDate()) + " - " + dateFormatterNote.format(pumpRecord.getSuspendBeforeDate()));
+        if (pumpRecord.isValidSUSPENDOFF())
+            sendStatus(ICON_RESUME + "Pump resumed insulin delivery approx: " + dateFormatterNote.format(pumpRecord.getSuspendAfterDate()) + " - " + dateFormatterNote.format(pumpRecord.getSuspendBeforeDate()));
+
+        if (pumpRecord.isValidSAGE())
+            sendStatus(ICON_CHANGE + "Sensor changed approx: " + dateFormatterNote.format(pumpRecord.getSageAfterDate()) + " - " + dateFormatterNote.format(pumpRecord.getSageBeforeDate()));
+        if (pumpRecord.isValidCAGE())
+            sendStatus(ICON_CHANGE + "Reservoir changed approx: " + dateFormatterNote.format(pumpRecord.getCageAfterDate()) + " - " + dateFormatterNote.format(pumpRecord.getCageBeforeDate()));
+        if (pumpRecord.isValidBATTERY())
+            sendStatus(ICON_CHANGE + "Pump battery changed approx: " + dateFormatterNote.format(pumpRecord.getBatteryAfterDate()) + " - " + dateFormatterNote.format(pumpRecord.getBatteryBeforeDate()));
+
+        if (pumpRecord.isValidALERT())
+            sendStatus(ICON_BELL + "Active alert on pump At: " + dateFormatter.format(pumpRecord.getAlertDate()));
+    }
+
+    private void sendStatusWarnings() {
+
+        if (dataStore.getPumpBatteryError() >= ERROR_PUMPBATTERY_AT) {
+            dataStore.clearPumpBatteryError();
+            sendStatus(ICON_WARN + "Warning: pump battery low");
+            if (configurationStore.getLowBatteryPollInterval() != configurationStore.getPollInterval())
+                sendStatus(ICON_SETTING + "Low battery poll interval: " + (configurationStore.getLowBatteryPollInterval() / 60000) + " minutes");
+        }
+
+        if (Math.abs(pumpOffset) > ERROR_PUMPCLOCK_MS)
+            dataStore.incPumpClockError();
+        if (dataStore.getPumpClockError() >= ERROR_PUMPCLOCK_AT) {
+            dataStore.clearPumpClockError();
+            sendStatus(ICON_WARN + "Warning: Time difference between Pump and Uploader excessive."
+                    + " Pump is over " + (Math.abs(pumpOffset) / 60000L) + " minutes " + (pumpOffset > 0 ? "ahead" : "behind") + " of time used by uploader.");
+            sendStatus(ICON_HELP + "The uploader phone/device should have the current time provided by network. Pump clock drifts forward and needs to be set to correct time occasionally.");
+        }
+
+        if (dataStore.getCommsError() >= ERROR_COMMS_AT) {
+            sendStatus(ICON_WARN + "Warning: multiple comms/timeout errors detected.");
+            sendStatus(ICON_HELP + "Try: disconnecting and reconnecting the Contour Next Link to phone / restarting phone / check pairing of CNL with Pump.");
+        }
+
+        if (dataStore.getPumpLostSensorError() >= ERROR_PUMPLOSTSENSOR_AT) {
+            dataStore.clearPumpLostSensorError();
+            sendStatus(ICON_WARN + "Warning: SGV is unavailable from pump often. The pump is missing transmissions from the sensor.");
+            sendStatus(ICON_HELP + "Keep pump on same side of body as sensor. Avoid using body sensor locations that can block radio signal.");
+        }
+
+        if (dataStore.getCommsConnectError() >= ERROR_CONNECT_AT * (configurationStore.isReducePollOnPumpAway() ? 2 : 1)) {
+            dataStore.clearCommsConnectError();
+            sendStatus(ICON_WARN + "Warning: connecting to pump is failing often.");
+            sendStatus(ICON_HELP + "Keep pump nearby to uploader phone/device. The body can block radio signals between pump and uploader.");
+        }
+
+        if (dataStore.getCommsSignalError() >= ERROR_SIGNAL_AT) {
+            dataStore.clearCommsSignalError();
+            sendStatus(ICON_WARN + "Warning: RSSI radio signal from pump is generally weak and may increase errors.");
+            sendStatus(ICON_HELP + "Keep pump nearby to uploader phone/device. The body can block radio signals between pump and uploader.");
+        }
+    }
+
+    private void RemoveOutdatedRecords() {
+        final RealmResults<PumpStatusEvent> results =
+                realm.where(PumpStatusEvent.class)
+                        .lessThan("eventDate", new Date(System.currentTimeMillis() - (48 * 60 * 60 * 1000)))
+                        .findAll();
+
+        if (results.size() > 0) {
+            realm.executeTransaction(new Realm.Transaction() {
+                @Override
+                public void execute(Realm realm) {
+                    // Delete all matches
+                    Log.d(TAG, "Deleting " + results.size() + " records from realm");
+                    results.deleteAllFromRealm();
+                }
+            });
+        }
+    }
+
+    // TODO - check on optimal use of Realm search+results as we make heavy use for validation
 
-                // TODO - Refactor warning system
-                if (dataStore.getCommsErrorCount() >= ERROR_COMMS_AT) {
-                    sendStatus(ICON_WARN + "Warning: multiple comms/timeout errors detected.");
-                    sendStatus(ICON_HELP + "Try: disconnecting and reconnecting the Contour Next Link to phone / restarting phone / check pairing of CNL with Pump.");
+    private void validatePumpRecord(PumpStatusEvent pumpRecord, PumpInfo activePump) {
+
+        int index;
+        Date uploaderStartDate = dataStore.getUploaderStartDate();
+
+        // TODO - pump validation is unused but will allow for future record manipulation when adding data from pump history message (gap fill)
+
+        // validate that this contains a new PUMP record
+        pumpRecord.setValidPUMP(true);
+
+        // TODO - cgm validation - handle sensor exceptions
+
+        // validate that this contains a new CGM record
+        if (pumpRecord.isCgmActive()) {
+            pumpRecord.setValidCGM(true);
+        }
+
+        // validate that this contains a new SGV record
+        if (pumpRecord.isCgmActive()) {
+            if (pumpRecord.getPumpDate().getTime() - pumpRecord.getCgmPumpDate().getTime() > POLL_PERIOD_MS + (POLL_GRACE_PERIOD_MS / 2))
+                pumpRecord.setOldSgvWhenNewExpected(true);
+            else if (!pumpRecord.isCgmWarmUp() && pumpRecord.getSgv() > 0) {
+                RealmResults<PumpStatusEvent> sgv_results = activePump.getPumpHistory()
+                        .where()
+                        .equalTo("cgmPumpDate", pumpRecord.getCgmPumpDate())
+                        .equalTo("validSGV", true)
+                        .findAll();
+                if (sgv_results.size() == 0)
+                    pumpRecord.setValidSGV(true);
+            }
+        }
+
+        // validate that this contains a new BGL record
+        if (pumpRecord.getRecentBGL() != 0) {
+            RealmResults<PumpStatusEvent> bgl_results = activePump.getPumpHistory()
+                    .where()
+                    .greaterThan("eventDate", new Date(System.currentTimeMillis() - (20 * 60 * 1000)))
+                    .equalTo("recentBGL", pumpRecord.getRecentBGL())
+                    .findAll();
+            if (bgl_results.size() == 0) {
+                pumpRecord.setValidBGL(true);
+            }
+        }
+
+        // TODO - square/dual stopped and new bolus started between poll snapshots, check realm history to handle this?
+
+        // validate that this contains a new BOLUS record
+        RealmResults<PumpStatusEvent> lastbolus_results = activePump.getPumpHistory()
+                .where()
+                .equalTo("lastBolusPumpDate", pumpRecord.getLastBolusPumpDate())
+                .equalTo("lastBolusReference", pumpRecord.getLastBolusReference())
+                .equalTo("validBolus", true)
+                .findAll();
+
+        if (lastbolus_results.size() == 0) {
+
+            RealmResults<PumpStatusEvent> bolusing_results = activePump.getPumpHistory()
+                    .where()
+                    .equalTo("bolusingReference", pumpRecord.getLastBolusReference())
+                    .greaterThan("bolusingMinutesRemaining", 10)   // if a manual normal bolus referred to here while square is being delivered it will show the remaining time for all bolusing
+                    .findAllSorted("eventDate", Sort.ASCENDING);
+
+            if (bolusing_results.size() > 0) {
+                long start = pumpRecord.getLastBolusPumpDate().getTime();
+                long start_bolusing = bolusing_results.first().getPumpDate().getTime();
+
+                // if pump battery is changed during square/dual bolus period the last bolus time will be set to this time (pump asks user to resume/cancel bolus)
+                // use bolusing start time when this is detected
+                if (start - start_bolusing > 10 * 60)
+                    start = start_bolusing;
+
+                long end = pumpRecord.getPumpDate().getTime();
+                long duration = start_bolusing - start + (bolusing_results.first().getBolusingMinutesRemaining() * 60000);
+                if (start + duration > end) // was square bolus stopped before expected duration?
+                    duration = end - start;
+
+                // check that this was a square bolus and not a normal bolus
+                if (duration > 10) {
+                    pumpRecord.setValidBolus(true);
+                    pumpRecord.setValidBolusSquare(true);
+                    pumpRecord.setLastBolusDate(new Date (start));
+                    pumpRecord.setLastBolusDuration((short) (duration / 60000));
                 }
-                if (dataStore.getCommsUnavailableThreshold() >= ERROR_UNAVAILABLE_AT) {
-                    dataStore.clearCommsUnavailableThreshold();
-                    sendStatus(ICON_WARN + "Warning: SGV unavailable from pump is happening often. The pump is missing transmissions from the sensor / in warm-up phase / environment radio noise.");
-                    sendStatus(ICON_HELP + "Keep pump on same side of body as sensor. Avoid using body sensor locations that can block radio signal. Sensor may be old / faulty and need changing (check pump graph for gaps).");
+            }
+
+            else if (pumpRecord.getLastBolusDate().getTime() >= uploaderStartDate.getTime()) {
+                pumpRecord.setValidBolus(true);
+                if (pumpRecord.getBolusingReference() == pumpRecord.getLastBolusReference()
+                        && pumpRecord.getBolusingMinutesRemaining() > 10) {
+                    pumpRecord.setValidBolusDual(true);
+                }
+            }
+        }
+
+        // validate that this contains a new TEMP BASAL record
+        // temp basal: rate / percentage can be set on pump for max duration of 24 hours / 1440 minutes
+        RealmResults<PumpStatusEvent> tempbasal_results = activePump.getPumpHistory()
+                .where()
+                .greaterThan("eventDate", new Date(System.currentTimeMillis() - (24 * 60 * 60 * 1000)))
+                .findAllSorted("eventDate", Sort.DESCENDING);
+        if (pumpRecord.getTempBasalMinutesRemaining() > 0) {
+            index = 0;
+            if (tempbasal_results.size() > 1) {
+                short minutes = pumpRecord.getTempBasalMinutesRemaining();
+                for (index = 0; index < tempbasal_results.size(); index++) {
+                    if (tempbasal_results.get(index).getTempBasalMinutesRemaining() < minutes ||
+                            tempbasal_results.get(index).getTempBasalPercentage() != pumpRecord.getTempBasalPercentage() ||
+                            tempbasal_results.get(index).getTempBasalRate() != pumpRecord.getTempBasalRate() ||
+                            tempbasal_results.get(index).isValidTEMPBASAL())
+                        break;
+                    minutes = tempbasal_results.get(index).getTempBasalMinutesRemaining();
                 }
-                if (dataStore.getCommsConnectThreshold() >= ERROR_CONNECT_AT * (configurationStore.isReducePollOnPumpAway() ? 2 : 1)) {
-                    dataStore.clearCommsConnectThreshold();
-                    sendStatus(ICON_WARN + "Warning: connecting to pump is failing often.");
-                    sendStatus(ICON_HELP + "Keep pump nearby to uploader phone/device. The body can block radio signals between pump and uploader.");
+            }
+            if (tempbasal_results.size() > 0)
+                if (!tempbasal_results.get(index).isValidTEMPBASAL() ||
+                        tempbasal_results.get(index).getTempBasalPercentage() != pumpRecord.getTempBasalPercentage() ||
+                        tempbasal_results.get(index).getTempBasalRate() != pumpRecord.getTempBasalRate()) {
+                    pumpRecord.setValidTEMPBASAL(true);
+                    pumpRecord.setTempBasalAfterDate(tempbasal_results.get(index).getEventDate());
+                    pumpRecord.setTempBasalBeforeDate(pumpRecord.getEventDate());
                 }
-                if (dataStore.getCommsSignalThreshold() >= ERROR_SIGNAL_AT) {
-                    dataStore.clearCommsSignalThreshold();
-                    sendStatus(ICON_WARN + "Warning: RSSI radio signal from pump is generally weak and may increase errors.");
-                    sendStatus(ICON_HELP + "Keep pump nearby to uploader phone/device. The body can block radio signals between pump and uploader.");
+        } else {
+            // check if stopped before expected duration
+            if (tempbasal_results.size() > 0)
+                if (pumpRecord.getPumpDate().getTime() - tempbasal_results.first().getPumpDate().getTime() - (tempbasal_results.first().getTempBasalMinutesRemaining() * 60 * 1000) < -60 * 1000) {
+                    pumpRecord.setValidTEMPBASAL(true);
+                    pumpRecord.setTempBasalAfterDate(tempbasal_results.first().getEventDate());
+                    pumpRecord.setTempBasalBeforeDate(pumpRecord.getEventDate());
                 }
+        }
 
+        // validate that this contains a new SUSPEND record
+        RealmResults<PumpStatusEvent> suspend_results =  activePump.getPumpHistory()
+                .where()
+                .greaterThan("eventDate", new Date(System.currentTimeMillis() - (2 * 60 * 60 * 1000)))
+                .equalTo("validSUSPEND", true)
+                .or()
+                .equalTo("validSUSPENDOFF", true)
+                .findAllSorted("eventDate", Sort.DESCENDING);
+
+        if (suspend_results.size() > 0) {
+            // new valid suspend - set temp basal for 0u 60m in NS
+            if (pumpRecord.isSuspended() && suspend_results.first().isValidSUSPENDOFF()) {
+                pumpRecord.setValidSUSPEND(true);
             }
-        } finally {
-            MedtronicCnlAlarmReceiver.completeWakefulIntent(intent);
+            // continuation valid suspend every 30m - set temp basal for 0u 60m in NS
+            else if (pumpRecord.isSuspended() && suspend_results.first().isValidSUSPEND() &&
+                    pumpRecord.getEventDate().getTime() - suspend_results.first().getEventDate().getTime() >= 30 * 60 * 1000) {
+                pumpRecord.setValidSUSPEND(true);
+            }
+            // valid suspendoff - set temp stopped in NS
+            else if (!pumpRecord.isSuspended() && suspend_results.first().isValidSUSPEND() &&
+                    pumpRecord.getEventDate().getTime() - suspend_results.first().getEventDate().getTime() <= 60 * 60 * 1000) {
+                pumpRecord.setValidSUSPENDOFF(true);
+                RealmResults<PumpStatusEvent> suspendended_results =  activePump.getPumpHistory()
+                        .where()
+                        .greaterThan("eventDate", new Date(System.currentTimeMillis() - (2 * 60 * 60 * 1000)))
+                        .findAllSorted("eventDate", Sort.DESCENDING);
+                pumpRecord.setSuspendAfterDate(suspendended_results.first().getEventDate());
+                pumpRecord.setSuspendBeforeDate(pumpRecord.getEventDate());
+            }
+        }
+        else if (pumpRecord.isSuspended()) {
+            pumpRecord.setValidSUSPEND(true);
         }
+
+        // absolute suspend start time approx to after-before range
+        if (pumpRecord.isValidSUSPEND()) {
+            RealmResults<PumpStatusEvent> suspendstarted_results =  activePump.getPumpHistory()
+                    .where()
+                    .greaterThan("eventDate", new Date(System.currentTimeMillis() - (12 * 60 * 60 * 1000)))
+                    .findAllSorted("eventDate", Sort.ASCENDING);
+            index = suspendstarted_results.size();
+            if (index > 0) {
+                while (index > 0) {
+                    index--;
+                    if (!suspendstarted_results.get(index).isSuspended())
+                        break;
+                }
+                pumpRecord.setSuspendAfterDate(suspendstarted_results.get(index).getEventDate());
+            }
+            else {
+                pumpRecord.setSuspendAfterDate(pumpRecord.getEventDate());
+            }
+            if (++index < suspendstarted_results.size())
+                pumpRecord.setSuspendBeforeDate(suspendstarted_results.get(index).getEventDate());
+            else
+                pumpRecord.setSuspendBeforeDate(pumpRecord.getEventDate());
+        }
+
+        // validate that this contains a new SAGE record
+        if (pumpRecord.isCgmWarmUp()) {
+            RealmResults<PumpStatusEvent> sage_results = activePump.getPumpHistory()
+                    .where()
+                    .greaterThan("eventDate", new Date(System.currentTimeMillis() - (130 * 60 * 1000)))
+                    .equalTo("validSAGE", true)
+                    .findAll();
+            if (sage_results.size() == 0) {
+                pumpRecord.setValidSAGE(true);
+                RealmResults<PumpStatusEvent> sagedate_results = activePump.getPumpHistory()
+                        .where()
+                        .greaterThan("eventDate", new Date(System.currentTimeMillis() - (6 * 60 * 60 * 1000)))
+                        .findAllSorted("eventDate", Sort.DESCENDING);
+                pumpRecord.setSageAfterDate(sagedate_results.first().getEventDate());
+                pumpRecord.setSageBeforeDate(pumpRecord.getEventDate());
+            }
+        } else if (pumpRecord.isCgmActive() && pumpRecord.getTransmitterBattery() == 100) {
+            // note: transmitter battery can fluctuate when on the edge of a state change
+            RealmResults<PumpStatusEvent> sagebattery_results = activePump.getPumpHistory()
+                    .where()
+                    .greaterThan("eventDate", new Date(System.currentTimeMillis() - (6 * 60 * 60 * 1000)))
+                    .equalTo("cgmActive", true)
+                    .lessThan("transmitterBattery", 50)
+                    .findAllSorted("eventDate", Sort.DESCENDING);
+            if (sagebattery_results.size() > 0) {
+                RealmResults<PumpStatusEvent> sage_valid_results = activePump.getPumpHistory()
+                        .where()
+                        .greaterThanOrEqualTo("eventDate", sagebattery_results.first().getEventDate())
+                        .equalTo("validSAGE", true)
+                        .findAll();
+                if (sage_valid_results.size() == 0) {
+                    pumpRecord.setValidSAGE(true);
+                    pumpRecord.setSageAfterDate(sagebattery_results.first().getEventDate());
+                    pumpRecord.setSageBeforeDate(pumpRecord.getEventDate());
+                }
+            }
+        }
+
+        // validate that this contains a new CAGE record
+        RealmResults<PumpStatusEvent> cage_results = activePump.getPumpHistory()
+                .where()
+                .greaterThan("eventDate", new Date(System.currentTimeMillis() - (6 * 60 * 60 * 1000)))
+                .lessThan("reservoirAmount", pumpRecord.getReservoirAmount())
+                .findAllSorted("eventDate", Sort.DESCENDING);
+        if (cage_results.size() > 0) {
+            RealmResults<PumpStatusEvent> cage_valid_results = activePump.getPumpHistory()
+                    .where()
+                    .greaterThanOrEqualTo("eventDate", cage_results.first().getEventDate())
+                    .equalTo("validCAGE", true)
+                    .findAll();
+            if (cage_valid_results.size() == 0) {
+                pumpRecord.setValidCAGE(true);
+                pumpRecord.setCageAfterDate(cage_results.first().getEventDate());
+                pumpRecord.setCageBeforeDate(pumpRecord.getEventDate());
+            }
+        }
+
+        // validate that this contains a new BATTERY record
+        RealmResults<PumpStatusEvent> battery_results = activePump.getPumpHistory()
+                .where()
+                .greaterThan("eventDate", new Date(System.currentTimeMillis() - (6 * 60 * 60 * 1000)))
+                .lessThan("batteryPercentage", pumpRecord.getBatteryPercentage())
+                .findAllSorted("eventDate", Sort.DESCENDING);
+        if (battery_results.size() > 0) {
+            RealmResults<PumpStatusEvent> battery_valid_results = activePump.getPumpHistory()
+                    .where()
+                    .greaterThanOrEqualTo("eventDate", battery_results.first().getEventDate())
+                    .equalTo("validBATTERY", true)
+                    .findAll();
+            if (battery_valid_results.size() == 0) {
+                pumpRecord.setValidBATTERY(true);
+                pumpRecord.setBatteryAfterDate(battery_results.first().getEventDate());
+                pumpRecord.setBatteryBeforeDate(pumpRecord.getEventDate());
+            }
+        }
+
+        // validate that this contains a new ALERT record
+        if (pumpRecord.getAlert() > 0) {
+            RealmResults<PumpStatusEvent> alert_results = activePump.getPumpHistory()
+                    .where()
+                    .greaterThan("eventDate", new Date(System.currentTimeMillis() - (6 * 60 * 60 * 1000)))
+                    .findAllSorted("eventDate", Sort.DESCENDING);
+            if (alert_results.size() > 0) {
+                if (alert_results.first().getAlert() != pumpRecord.getAlert())
+                    pumpRecord.setValidALERT(true);
+            }
+        }
+
     }
 
-    // TODO - Refactor polling system and make super clear how polling is calculated and why certain precautions are needed
-    private void scheduleNextPoll(long timePollStarted, long timeLastGoodSGV, long pollInterval) {
-        // smart polling and pump-sensor poll clash detection
-        long now = System.currentTimeMillis();
-        long lastActualPollTime = timePollStarted;
-        if (timeLastGoodSGV > 0) {
-            lastActualPollTime = timeLastGoodSGV + POLL_GRACE_PERIOD_MS + (POLL_PERIOD_MS * ((now - timeLastGoodSGV + POLL_GRACE_PERIOD_MS) / POLL_PERIOD_MS));
+    // pollInterval: default = POLL_PERIOD_MS (time to pump cgm reading)
+    //
+    // Can be requested at a shorter or longer interval, used to request a retry before next expected cgm data or to extend poll times due to low pump battery.
+    // Requests the next poll based on the actual time last cgm data was available on the pump and adding the interval
+    // if this time is already stale then the next actual cgm time will be used
+    // Any poll time request that falls within the pre-grace/grace period will be pushed to the next safe time slot
+
+    private long requestPollTime(long lastPoll, long pollInterval) {
+
+        int pumpCgmNA = dataStore.getPumpCgmNA();
+
+        RealmResults<PumpStatusEvent> cgmresults = realm.where(PumpStatusEvent.class)
+                .greaterThan("eventDate", new Date(System.currentTimeMillis() - (24 * 60 * 1000)))
+                .equalTo("validCGM", true)
+                .findAllSorted("cgmDate", Sort.DESCENDING);
+        long timeLastCGM = 0;
+        if (cgmresults.size() > 0) {
+            timeLastCGM = cgmresults.first().getCgmDate().getTime();
         }
+
+        long now = System.currentTimeMillis();
+        long lastActualPollTime = lastPoll;
+        if (timeLastCGM > 0)
+            lastActualPollTime = timeLastCGM + POLL_GRACE_PERIOD_MS + (POLL_PERIOD_MS * ((now - timeLastCGM + POLL_GRACE_PERIOD_MS) / POLL_PERIOD_MS));
         long nextActualPollTime = lastActualPollTime + POLL_PERIOD_MS;
         long nextRequestedPollTime = lastActualPollTime + pollInterval;
-        // check if request is really needed
-        if (nextRequestedPollTime - now < 10000L) {
+
+        // check if requested poll is stale
+        if (nextRequestedPollTime - now < 10 * 1000)
             nextRequestedPollTime = nextActualPollTime;
-        }
-        // extended unavailable SGV may be due to clash with the current polling time
-        // while we wait for a good SGV event, polling is auto adjusted by offsetting the next poll based on miss count
-        if (dataStore.getUnavailableSGVCount() > 0) {
-            if (timeLastGoodSGV == 0) {
-                nextRequestedPollTime += POLL_PERIOD_MS / 5L; // if there is a uploader/sensor poll clash on startup then this will push the next attempt out by 60 seconds
-            } else if (dataStore.getUnavailableSGVCount() > 2) {
-                sendStatus(ICON_WARN + "Warning: No SGV available from pump for " + dataStore.getUnavailableSGVCount() + " attempts");
-                long offsetPollTime = ((long) ((dataStore.getUnavailableSGVCount() - 2) % 5)) * (POLL_PERIOD_MS / 10L); // adjust poll time in 1/10 steps to avoid potential poll clash (max adjustment at 5/10)
-                sendStatus("Adjusting poll: "  + dateFormatter.format(nextRequestedPollTime) +  " +" + (offsetPollTime / 1000) + "sec");
-                nextRequestedPollTime += offsetPollTime;
-            }
-        }
+
+        // extended unavailable cgm may be due to clash with the current polling time
+        // while we wait for a cgm event, polling is auto adjusted by offsetting the next poll based on miss count
+
+        if (timeLastCGM == 0)
+            nextRequestedPollTime += 15 * 1000; // push poll time forward to avoid potential clash when no previous poll time available to sync with
+        else if (pumpCgmNA >= POLL_ANTI_CLASH)
+            nextRequestedPollTime += (((pumpCgmNA - POLL_ANTI_CLASH) % 3) + 1) * 30 * 1000; // adjust poll time in 30 second steps to avoid potential poll clash (adjustment: poll+30s / poll+60s / poll+90s)
+
         // check if requested poll time is too close to next actual poll time
         if (nextRequestedPollTime > nextActualPollTime - POLL_GRACE_PERIOD_MS - POLL_PRE_GRACE_PERIOD_MS
                 && nextRequestedPollTime < nextActualPollTime) {
             nextRequestedPollTime = nextActualPollTime;
         }
-        MedtronicCnlAlarmManager.setAlarm(nextRequestedPollTime);
-        sendStatus("Next poll due at: " + dateFormatter.format(nextRequestedPollTime));
+
+        return nextRequestedPollTime;
+    }
+
+    private long checkPollTime() {
+        long due = 0;
+
+        RealmResults<PumpStatusEvent> cgmresults = realm.where(PumpStatusEvent.class)
+                .greaterThan("eventDate", new Date(System.currentTimeMillis() - (24 * 60 * 1000)))
+                .equalTo("validCGM", true)
+                .findAllSorted("cgmDate", Sort.DESCENDING);
+
+        if (cgmresults.size() > 0) {
+            long now = System.currentTimeMillis();
+            long timeLastCGM = cgmresults.first().getCgmDate().getTime();
+            long timePollExpected = timeLastCGM + POLL_PERIOD_MS + POLL_GRACE_PERIOD_MS + (POLL_PERIOD_MS * ((now - 1000L - (timeLastCGM + POLL_GRACE_PERIOD_MS)) / POLL_PERIOD_MS));
+            // avoid polling when too close to sensor-pump comms
+            if (((timePollExpected - now) > 5000L) && ((timePollExpected - now) < (POLL_PRE_GRACE_PERIOD_MS + POLL_GRACE_PERIOD_MS)))
+                due = timePollExpected;
+        }
+
+        return due;
     }
 
     /**
@@ -549,4 +932,4 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received
 
         public static final String EXTENDED_DATA = "info.nightscout.android.medtronic.service.DATA";
     }
-}
+}
\ No newline at end of file
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 75847c83ce358679ce2ce14bcc0ae728675d4b13..f0d9469626756391b0e1eea41125528b65cfa8e1 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,7 +3,6 @@ package info.nightscout.android.model.medtronicNg;
 import java.util.Date;
 
 import io.realm.RealmObject;
-import io.realm.annotations.Ignore;
 import io.realm.annotations.Index;
 
 /**
@@ -11,20 +10,31 @@ import io.realm.annotations.Index;
  */
 public class PumpStatusEvent 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 Date eventDate; // The actual time of the this event (uploader device time)
+    private Date pumpDate; // The time on the pump at the time of the this event
+    private long pumpTimeOffset; // millis the pump is ahead
+
     private String deviceName;
 
     // Data from the Medtronic Pump Status message
+
+    private byte pumpStatus;
+    private byte cgmStatus;
     private boolean suspended;
-    private boolean bolusing;
+    private boolean bolusingNormal;
+    private boolean bolusingSquare;
+    private boolean bolusingDual;
     private boolean deliveringInsulin;
     private boolean tempBasalActive;
     private boolean cgmActive;
+    private boolean cgmCalibrating;
+    private boolean cgmCalibrationComplete;
+    private boolean cgmException;
+    private boolean cgmWarmUp;
     private byte activeBasalPattern;
     private float basalRate;
     private float tempBasalRate;
-    private byte tempBasalPercentage;
+    private short tempBasalPercentage;
     private short tempBasalMinutesRemaining;
     private float basalUnitsDeliveredToday;
     private short batteryPercentage;
@@ -32,15 +42,55 @@ public class PumpStatusEvent extends RealmObject {
     private short minutesOfInsulinRemaining; // 25h == "more than 1 day"
     private float activeInsulin;
     private int sgv;
-    private Date sgvDate;
+    private Date cgmDate;
+    private Date cgmPumpDate;
+    private byte cgmExceptionType;
     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
+    private int recentBGL; // in mg/dL. 0 means no recent finger bg reading.
+    private short alert;
+    private Date alertDate;
+    private Date alertPumpDate;
+    private float bolusingDelivered;
+    private short bolusingMinutesRemaining;
+    private short bolusingReference;
+    private float lastBolusAmount;
+    private Date lastBolusDate;
+    private Date lastBolusPumpDate;
+    private short lastBolusDuration;
+    private short lastBolusReference;
+    private byte transmitterBattery;
+    private byte transmitterControl;
+    private short calibrationDueMinutes;
+    private float sensorRateOfChange;
+
+    private boolean oldSgvWhenNewExpected = false;
+
+    private boolean validPUMP = false;
+    private boolean validCGM = false;
+    private boolean validSGV = false;
+    private boolean validBGL = false;
+    private boolean validBolus = false;
+    private boolean validBolusDual = false;
+    private boolean validBolusSquare = false;
+    private boolean validALERT = false;
+    private boolean validSUSPEND = false;
+    private boolean validSUSPENDOFF = false;
+    private Date suspendAfterDate;
+    private Date suspendBeforeDate;
+    private boolean validTEMPBASAL = false;
+    private Date tempBasalAfterDate;
+    private Date tempBasalBeforeDate;
+    private boolean validCAGE = false;
+    private Date cageAfterDate;
+    private Date cageBeforeDate;
+    private boolean validSAGE = false;
+    private Date sageAfterDate;
+    private Date sageBeforeDate;
+    private boolean validBATTERY = false;
+    private Date batteryAfterDate;
+    private Date batteryBeforeDate;
 
     @Index
     private boolean uploaded = false;
@@ -50,12 +100,14 @@ public class PumpStatusEvent extends RealmObject {
         this.eventDate = new Date();
     }
 
+    public void setEventDate(Date eventDate) {
+        this.eventDate = eventDate;
+    }
+
     public Date getEventDate() {
         return eventDate;
     }
 
-    // No EventDate setter. The eventDate is set at the time that the PumpStatusEvent is created.
-
     public Date getPumpDate() {
         return pumpDate;
     }
@@ -131,12 +183,12 @@ public class PumpStatusEvent extends RealmObject {
         return recentBolusWizard;
     }
 
-    public int getBolusWizardBGL() {
-        return bolusWizardBGL;
+    public int getRecentBGL() {
+        return recentBGL;
     }
 
-    public void setBolusWizardBGL(int bolusWizardBGL) {
-        this.bolusWizardBGL = bolusWizardBGL;
+    public void setRecentBGL(int recentBGL) {
+        this.recentBGL = recentBGL;
     }
 
     public boolean isUploaded() {
@@ -147,6 +199,207 @@ public class PumpStatusEvent extends RealmObject {
         this.uploaded = uploaded;
     }
 
+    public boolean isOldSgvWhenNewExpected() {
+        return oldSgvWhenNewExpected;
+    }
+
+    public void setOldSgvWhenNewExpected(boolean oldSgvWhenNewExpected) {
+        this.oldSgvWhenNewExpected = oldSgvWhenNewExpected;
+    }
+
+    public boolean isValidPUMP() {
+        return validPUMP;
+    }
+
+    public void setValidPUMP(boolean validPUMP) {
+        this.validPUMP = validPUMP;
+    }
+
+    public boolean isValidCGM() {
+        return validCGM;
+    }
+
+    public void setValidCGM(boolean validCGM) {
+        this.validCGM = validCGM;
+    }
+
+    public boolean isValidSGV() {
+        return validSGV;
+    }
+
+    public void setValidSGV(boolean validSGV) {
+        this.validSGV = validSGV;
+    }
+
+    public boolean isValidBGL() {
+        return validBGL;
+    }
+
+    public void setValidBGL(boolean validBGL) {
+        this.validBGL = validBGL;
+    }
+
+    public boolean isValidBolus() {
+        return validBolus;
+    }
+
+    public void setValidBolus(boolean validBolus) {
+        this.validBolus = validBolus;
+    }
+
+    public boolean isValidBolusDual() {
+        return validBolusDual;
+    }
+
+    public void setValidBolusDual(boolean validBolusDual) {
+        this.validBolusDual = validBolusDual;
+    }
+
+    public boolean isValidBolusSquare() {
+        return validBolusSquare;
+    }
+
+    public void setValidBolusSquare(boolean validBolusSquare) {
+        this.validBolusSquare = validBolusSquare;
+    }
+
+    public boolean isValidALERT() {
+        return validALERT;
+    }
+
+    public void setValidALERT(boolean validALERT) {
+        this.validALERT = validALERT;
+    }
+
+    public boolean isValidSUSPEND() {
+        return validSUSPEND;
+    }
+
+    public void setValidSUSPEND(boolean validSUSPEND) {
+        this.validSUSPEND = validSUSPEND;
+    }
+
+    public boolean isValidSUSPENDOFF() {
+        return validSUSPENDOFF;
+    }
+
+    public void setValidSUSPENDOFF(boolean validSUSPENDOFF) {
+        this.validSUSPENDOFF = validSUSPENDOFF;
+    }
+
+    public Date getSuspendAfterDate() {
+        return suspendAfterDate;
+    }
+
+    public void setSuspendAfterDate(Date suspendAfterDate) {
+        this.suspendAfterDate = suspendAfterDate;
+    }
+
+    public Date getSuspendBeforeDate() {
+        return suspendBeforeDate;
+    }
+
+    public void setSuspendBeforeDate(Date suspendBeforeDate) {
+        this.suspendBeforeDate = suspendBeforeDate;
+    }
+
+
+    public boolean isValidTEMPBASAL() {
+        return validTEMPBASAL;
+    }
+
+    public void setValidTEMPBASAL(boolean validTEMPBASAL) {
+        this.validTEMPBASAL = validTEMPBASAL;
+    }
+
+    public Date getTempBasalAfterDate() {
+        return tempBasalAfterDate;
+    }
+
+    public void setTempBasalAfterDate(Date tempBasalAfterDate) {
+        this.tempBasalAfterDate = tempBasalAfterDate;
+    }
+
+    public Date getTempBasalBeforeDate() {
+        return tempBasalBeforeDate;
+    }
+
+    public void setTempBasalBeforeDate(Date tempBasalBeforeDate) {
+        this.tempBasalBeforeDate = tempBasalBeforeDate;
+    }
+
+    public boolean isValidCAGE() {
+        return validCAGE;
+    }
+
+    public void setValidCAGE(boolean validCAGE) {
+        this.validCAGE = validCAGE;
+    }
+
+    public Date getCageAfterDate() {
+        return cageAfterDate;
+    }
+
+    public void setCageAfterDate(Date cageAfterDate) {
+        this.cageAfterDate = cageAfterDate;
+    }
+
+    public Date getCageBeforeDate() {
+        return cageBeforeDate;
+    }
+
+    public void setCageBeforeDate(Date cageBeforeDate) {
+        this.cageBeforeDate = cageBeforeDate;
+    }
+
+    public boolean isValidSAGE() {
+        return validSAGE;
+    }
+
+    public void setValidSAGE(boolean validSAGE) {
+        this.validSAGE = validSAGE;
+    }
+
+    public Date getSageAfterDate() {
+        return sageAfterDate;
+    }
+
+    public void setSageAfterDate(Date sageAfterDate) {
+        this.sageAfterDate = sageAfterDate;
+    }
+
+    public Date getSageBeforeDate() {
+        return sageBeforeDate;
+    }
+
+    public void setSageBeforeDate(Date sageBeforeDate) {
+        this.sageBeforeDate = sageBeforeDate;
+    }
+
+    public boolean isValidBATTERY() {
+        return validBATTERY;
+    }
+
+    public void setValidBATTERY(boolean validBATTERY) {
+        this.validBATTERY = validBATTERY;
+    }
+
+    public Date getBatteryAfterDate() {
+        return batteryAfterDate;
+    }
+
+    public void setBatteryAfterDate(Date batteryAfterDate) {
+        this.batteryAfterDate = batteryAfterDate;
+    }
+
+    public Date getBatteryBeforeDate() {
+        return batteryBeforeDate;
+    }
+
+    public void setBatteryBeforeDate(Date batteryBeforeDate) {
+        this.batteryBeforeDate = batteryBeforeDate;
+    }
+
     public boolean isSuspended() {
         return suspended;
     }
@@ -155,12 +408,28 @@ public class PumpStatusEvent extends RealmObject {
         this.suspended = suspended;
     }
 
-    public boolean isBolusing() {
-        return bolusing;
+    public boolean isBolusingNormal() {
+        return bolusingNormal;
+    }
+
+    public void setBolusingNormal(boolean bolusingNormal) {
+        this.bolusingNormal = bolusingNormal;
+    }
+
+    public boolean isBolusingSquare() {
+        return bolusingSquare;
+    }
+
+    public void setBolusingSquare(boolean bolusingSquare) {
+        this.bolusingSquare = bolusingSquare;
     }
 
-    public void setBolusing(boolean bolusing) {
-        this.bolusing = bolusing;
+    public boolean isBolusingDual() {
+        return bolusingDual;
+    }
+
+    public void setBolusingDual(boolean bolusingDual) {
+        this.bolusingDual = bolusingDual;
     }
 
     public boolean isDeliveringInsulin() {
@@ -211,11 +480,11 @@ public class PumpStatusEvent extends RealmObject {
         this.tempBasalRate = tempBasalRate;
     }
 
-    public byte getTempBasalPercentage() {
+    public short getTempBasalPercentage() {
         return tempBasalPercentage;
     }
 
-    public void setTempBasalPercentage(byte tempBasalPercentage) {
+    public void setTempBasalPercentage(short tempBasalPercentage) {
         this.tempBasalPercentage = tempBasalPercentage;
     }
 
@@ -243,28 +512,28 @@ public class PumpStatusEvent extends RealmObject {
         this.minutesOfInsulinRemaining = minutesOfInsulinRemaining;
     }
 
-    public Date getSgvDate() {
-        return sgvDate;
+    public Date getCgmDate() {
+        return cgmDate;
     }
 
-    public void setSgvDate(Date sgvDate) {
-        this.sgvDate = sgvDate;
+    public void setCgmDate(Date cgmDate) {
+        this.cgmDate = cgmDate;
     }
 
-    public boolean isLowSuspendActive() {
-        return lowSuspendActive;
+    public Date getCgmPumpDate() {
+        return cgmPumpDate;
     }
 
-    public void setLowSuspendActive(boolean lowSuspendActive) {
-        this.lowSuspendActive = lowSuspendActive;
+    public void setCgmPumpDate(Date cgmPumpDate) {
+        this.cgmPumpDate = cgmPumpDate;
     }
 
-    public boolean isRecentBolusWizard() {
-        return recentBolusWizard;
+    public boolean isLowSuspendActive() {
+        return lowSuspendActive;
     }
 
-    public void setRecentBolusWizard(boolean recentBolusWizard) {
-        this.recentBolusWizard = recentBolusWizard;
+    public void setLowSuspendActive(boolean lowSuspendActive) {
+        this.lowSuspendActive = lowSuspendActive;
     }
 
     public long getPumpTimeOffset() {
@@ -275,17 +544,202 @@ public class PumpStatusEvent extends RealmObject {
         this.pumpTimeOffset = pumpTimeOffset;
     }
 
+    public int getPumpStatus() {
+        return pumpStatus;
+    }
+
+    public void setPumpStatus(byte pumpStatus) {
+        this.pumpStatus = pumpStatus;
+    }
+
+    public int getCgmStatus() {
+        return cgmStatus;
+    }
+
+    public void setCgmStatus(byte cgmStatus) {
+        this.cgmStatus = cgmStatus;
+    }
+
+    public boolean isCgmCalibrating() {
+        return cgmCalibrating;
+    }
+
+    public void setCgmCalibrating(boolean cgmCalibrating) {
+        this.cgmCalibrating = cgmCalibrating;
+    }
+
+    public boolean isCgmCalibrationComplete() {
+        return cgmCalibrationComplete;
+    }
+
+    public void setCgmCalibrationComplete(boolean cgmCalibrationComplete) {
+        this.cgmCalibrationComplete = cgmCalibrationComplete;
+    }
+
+    public boolean isCgmException() {
+        return cgmException;
+    }
+
+    public void setCgmException(boolean cgmException) {
+        this.cgmException = cgmException;
+    }
+
+    public boolean isCgmWarmUp() {
+        return cgmWarmUp;
+    }
+
+    public void setCgmWarmUp(boolean cgmWarmUp) {
+        this.cgmWarmUp = cgmWarmUp;
+    }
+
+    public byte getCgmExceptionType() {
+        return cgmExceptionType;
+    }
+
+    public void setCgmExceptionType(byte cgmExceptionType) {
+        this.cgmExceptionType = cgmExceptionType;
+    }
+
+    public short getAlert() {
+        return alert;
+    }
+
+    public void setAlert(short alert) {
+        this.alert = alert;
+    }
+
+    public Date getAlertDate() {
+        return alertDate;
+    }
+
+    public void setAlertDate(Date alertDate) {
+        this.alertDate = alertDate;
+    }
+
+    public Date getAlertPumpDate() {
+        return alertPumpDate;
+    }
+
+    public void setAlertPumpDate(Date alertPumpDate) {
+        this.alertPumpDate = alertPumpDate;
+    }
+
+    public float getBolusingDelivered() {
+        return bolusingDelivered;
+    }
+
+    public void setBolusingDelivered(float bolusingDelivered) {
+        this.bolusingDelivered = bolusingDelivered;
+    }
+
+    public short getBolusingMinutesRemaining() {
+        return bolusingMinutesRemaining;
+    }
+
+    public void setBolusingMinutesRemaining(short bolusingMinutesRemaining) {
+        this.bolusingMinutesRemaining = bolusingMinutesRemaining;
+    }
+
+    public short getBolusingReference() {
+        return bolusingReference;
+    }
+
+    public void setBolusingReference(short bolusingReference) {
+        this.bolusingReference = bolusingReference;
+    }
+
+    public float getLastBolusAmount() {
+        return lastBolusAmount;
+    }
+
+    public void setLastBolusAmount(float lastBolusAmount) {
+        this.lastBolusAmount = lastBolusAmount;
+    }
+
+    public Date getLastBolusDate() {
+        return lastBolusDate;
+    }
+
+    public void setLastBolusDate(Date lastBolusDate) {
+        this.lastBolusDate = lastBolusDate;
+    }
+
+    public Date getLastBolusPumpDate() {
+        return lastBolusPumpDate;
+    }
+
+    public void setLastBolusPumpDate(Date lastBolusPumpDate) {
+        this.lastBolusPumpDate = lastBolusPumpDate;
+    }
+
+    public short getLastBolusReference() {
+        return lastBolusReference;
+    }
+
+    public void setLastBolusReference(short lastBolusReference) {
+        this.lastBolusReference = lastBolusReference;
+    }
+
+    public short getLastBolusDuration() {
+        return lastBolusDuration;
+    }
+
+    public void setLastBolusDuration(short lastBolusDuration) {
+        this.lastBolusDuration = lastBolusDuration;
+    }
+
+    public byte getTransmitterBattery() {
+        return transmitterBattery;
+    }
+
+    public void setTransmitterBattery(byte transmitterBattery) {
+        this.transmitterBattery = transmitterBattery;
+    }
+
+    public byte getTransmitterControl() {
+        return transmitterControl;
+    }
+
+    public void setTransmitterControl(byte transmitterControl) {
+        this.transmitterControl = transmitterControl;
+    }
+
+    public short getCalibrationDueMinutes() {
+        return calibrationDueMinutes;
+    }
+
+    public void setCalibrationDueMinutes(short calibrationDueMinutes) {
+        this.calibrationDueMinutes = calibrationDueMinutes;
+    }
+
+    public float getSensorRateOfChange() {
+        return sensorRateOfChange;
+    }
+
+    public void setSensorRateOfChange(float sensorRateOfChange) {
+        this.sensorRateOfChange = sensorRateOfChange;
+    }
+
     @Override
     public String toString() {
         return "PumpStatusEvent{" +
                 "eventDate=" + eventDate +
                 ", pumpDate=" + pumpDate +
+                ", pumpTimeOffset=" + pumpTimeOffset +
                 ", deviceName='" + deviceName + '\'' +
+                ", pumpStatus=" + pumpStatus +
+                ", cgmStatus=" + cgmStatus +
                 ", suspended=" + suspended +
-                ", bolusing=" + bolusing +
+                ", bolusingNormal=" + bolusingNormal +
+                ", bolusingSquare=" + bolusingSquare +
+                ", bolusingDual=" + bolusingDual +
                 ", deliveringInsulin=" + deliveringInsulin +
                 ", tempBasalActive=" + tempBasalActive +
                 ", cgmActive=" + cgmActive +
+                ", cgmCalibrating=" + cgmCalibrating +
+                ", cgmCalibrationComplete=" + cgmCalibrationComplete +
+                ", cgmException=" + cgmException +
+                ", cgmWarmUp=" + cgmWarmUp +
                 ", activeBasalPattern=" + activeBasalPattern +
                 ", basalRate=" + basalRate +
                 ", tempBasalRate=" + tempBasalRate +
@@ -297,12 +751,53 @@ public class PumpStatusEvent extends RealmObject {
                 ", minutesOfInsulinRemaining=" + minutesOfInsulinRemaining +
                 ", activeInsulin=" + activeInsulin +
                 ", sgv=" + sgv +
-                ", sgvDate=" + sgvDate +
+                ", cgmDate=" + cgmDate +
+                ", cgmPumpDate=" + cgmPumpDate +
+                ", cgmExceptionType=" + cgmExceptionType +
                 ", lowSuspendActive=" + lowSuspendActive +
                 ", cgmTrend='" + cgmTrend + '\'' +
                 ", recentBolusWizard=" + recentBolusWizard +
-                ", bolusWizardBGL=" + bolusWizardBGL +
-                ", pumpTimeOffset=" + pumpTimeOffset +
+                ", recentBGL=" + recentBGL +
+                ", alert=" + alert +
+                ", alertDate=" + alertDate +
+                ", alertPumpDate=" + alertPumpDate +
+                ", bolusingDelivered=" + bolusingDelivered +
+                ", bolusingMinutesRemaining=" + bolusingMinutesRemaining +
+                ", bolusingReference=" + bolusingReference +
+                ", lastBolusAmount=" + lastBolusAmount +
+                ", lastBolusDate=" + lastBolusDate +
+                ", lastBolusPumpDate=" + lastBolusPumpDate +
+                ", lastBolusDuration=" + lastBolusDuration +
+                ", lastBolusReference=" + lastBolusReference +
+                ", transmitterBattery=" + transmitterBattery +
+                ", transmitterControl=" + transmitterControl +
+                ", calibrationDueMinutes=" + calibrationDueMinutes +
+                ", sensorRateOfChange=" + sensorRateOfChange +
+                ", oldSgvWhenNewExpected=" + oldSgvWhenNewExpected +
+                ", validPUMP=" + validPUMP +
+                ", validCGM=" + validCGM +
+                ", validSGV=" + validSGV +
+                ", validBGL=" + validBGL +
+                ", validBolus=" + validBolus +
+                ", validBolusDual=" + validBolusDual +
+                ", validBolusSquare=" + validBolusSquare +
+                ", validALERT=" + validALERT +
+                ", validSUSPEND=" + validSUSPEND +
+                ", validSUSPENDOFF=" + validSUSPENDOFF +
+                ", suspendAfterDate=" + suspendAfterDate +
+                ", suspendBeforeDate=" + suspendBeforeDate +
+                ", validTEMPBASAL=" + validTEMPBASAL +
+                ", tempBasalAfterDate=" + tempBasalAfterDate +
+                ", tempBasalBeforeDate=" + tempBasalBeforeDate +
+                ", validCAGE=" + validCAGE +
+                ", cageAfterDate=" + cageAfterDate +
+                ", cageBeforeDate=" + cageBeforeDate +
+                ", validSAGE=" + validSAGE +
+                ", sageAfterDate=" + sageAfterDate +
+                ", sageBeforeDate=" + sageBeforeDate +
+                ", validBATTERY=" + validBATTERY +
+                ", batteryAfterDate=" + batteryAfterDate +
+                ", batteryBeforeDate=" + batteryBeforeDate +
                 ", uploaded=" + uploaded +
                 '}';
     }
diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java
index 819f82e3923c37694a2ba17447dac2d82543ee0b..06f7f79eccf7b415c588be5e9a65e966f5cf113a 100644
--- a/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java
+++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java
@@ -1,7 +1,5 @@
 package info.nightscout.android.upload.nightscout;
 
-import android.util.Log;
-
 import java.io.UnsupportedEncodingException;
 import java.math.BigDecimal;
 import java.security.MessageDigest;
@@ -16,11 +14,22 @@ import info.nightscout.android.upload.nightscout.serializer.EntriesSerializer;
 
 import android.support.annotation.NonNull;
 
+import info.nightscout.android.utils.ConfigurationStore;
 import info.nightscout.api.UploadApi;
-import info.nightscout.api.GlucoseEndpoints;
-import info.nightscout.api.BolusEndpoints.BolusEntry;
-import info.nightscout.api.GlucoseEndpoints.GlucoseEntry;
-import info.nightscout.api.BolusEndpoints;
+import info.nightscout.api.SgvEndpoints;
+import info.nightscout.api.SgvEndpoints.SgvEntry;
+import info.nightscout.api.MbgEndpoints;
+import info.nightscout.api.MbgEndpoints.MbgEntry;
+import info.nightscout.api.TreatmentEndpoints;
+import info.nightscout.api.TreatmentEndpoints.TreatmentEntry;
+import info.nightscout.api.TempBasalRateEndpoints.TempBasalRateEntry;
+import info.nightscout.api.TempBasalRateEndpoints;
+import info.nightscout.api.TempBasalPercentEndpoints;
+import info.nightscout.api.TempBasalPercentEndpoints.TempBasalPercentEntry;
+import info.nightscout.api.TempBasalCancelEndpoints;
+import info.nightscout.api.TempBasalCancelEndpoints.TempBasalCancelEntry;
+import info.nightscout.api.NoteEndpoints;
+import info.nightscout.api.NoteEndpoints.NoteEntry;
 import info.nightscout.api.DeviceEndpoints;
 import info.nightscout.api.DeviceEndpoints.Iob;
 import info.nightscout.api.DeviceEndpoints.Battery;
@@ -30,39 +39,43 @@ import info.nightscout.api.DeviceEndpoints.DeviceStatus;
 import okhttp3.ResponseBody;
 import retrofit2.Response;
 
+import static info.nightscout.android.medtronic.MainActivity.MMOLXLFACTOR;
+
 class NightScoutUpload {
 
     private static final String TAG = NightscoutUploadIntentService.class.getSimpleName();
     private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
+    private static final SimpleDateFormat NOTE_DATE_FORMAT = new SimpleDateFormat("E h:mm a", Locale.getDefault());
 
     NightScoutUpload() {
 
     }
 
-    Boolean doRESTUpload(String url,
+    boolean doRESTUpload(String url,
                          String secret,
+                         boolean treatments,
                          int uploaderBatteryLevel,
-                         List<PumpStatusEvent> records) {
-        Boolean success = false;
-        try {
-            success = isUploaded(records, url, secret, uploaderBatteryLevel);
-        } catch (Exception e) {
-            Log.e(TAG, "Unable to do REST API Upload to: " + url, e);
-        }
-        return success;
+                         List<PumpStatusEvent> records) throws Exception {
+        return isUploaded(records, url, secret, treatments, uploaderBatteryLevel);
     }
 
-
     private boolean isUploaded(List<PumpStatusEvent> records,
                                String baseURL,
                                String secret,
+                               boolean treatments,
                                int uploaderBatteryLevel) throws Exception {
 
         UploadApi uploadApi = new UploadApi(baseURL, formToken(secret));
 
-        boolean eventsUploaded = uploadEvents(uploadApi.getGlucoseEndpoints(),
-                uploadApi.getBolusEndpoints(),
-                records);
+        boolean eventsUploaded = uploadEvents(
+                uploadApi.getSgvEndpoints(),
+                uploadApi.getMbgEndpoints(),
+                uploadApi.getTreatmentEndpoints(),
+                uploadApi.getTempBasalRateEndpoints(),
+                uploadApi.getTempBasalPercentEndpoints(),
+                uploadApi.getTempBasalCancelEndpoints(),
+                uploadApi.getNoteEndpoints(),
+                records, treatments);
 
         boolean deviceStatusUploaded = uploadDeviceStatus(uploadApi.getDeviceEndpoints(),
                 uploaderBatteryLevel, records);
@@ -70,48 +83,200 @@ class NightScoutUpload {
         return eventsUploaded && deviceStatusUploaded;
     }
 
-    private boolean uploadEvents(GlucoseEndpoints glucoseEndpoints,
-                                 BolusEndpoints bolusEndpoints,
-                                 List<PumpStatusEvent> records) throws Exception {
-
-
-        List<GlucoseEntry> glucoseEntries = new ArrayList<>();
-        List<BolusEntry> bolusEntries = new ArrayList<>();
+    private boolean uploadEvents(SgvEndpoints sgvEndpoints,
+                                 MbgEndpoints mbgEndpoints,
+                                 TreatmentEndpoints treatmentEndpoints,
+                                 TempBasalRateEndpoints tempBasalRateEndpoints,
+                                 TempBasalPercentEndpoints tempBasalPercentEndpoints,
+                                 TempBasalCancelEndpoints tempBasalCancelEndpoints,
+                                 NoteEndpoints noteEndpoints,
+                                 List<PumpStatusEvent> records,
+                                 boolean treatments) throws Exception {
+
+
+        List<SgvEntry> sgvEntries = new ArrayList<>();
+        List<MbgEntry> mbgEntries = new ArrayList<>();
+        List<TreatmentEntry> treatmentEntries = new ArrayList<>();
+        List<TempBasalRateEntry> tempBasalRateEntries = new ArrayList<>();
+        List<TempBasalPercentEntry> tempBasalPercentEntries = new ArrayList<>();
+        List<TempBasalCancelEntry> tempBasalCancelEntries = new ArrayList<>();
+        List<NoteEntry> noteEntries = new ArrayList<>();
 
         for (PumpStatusEvent record : records) {
 
-            GlucoseEntry glucoseEntry = new GlucoseEntry();
-
-            glucoseEntry.setType("sgv");
-            glucoseEntry.setDirection(EntriesSerializer.getDirectionStringStatus(record.getCgmTrend()));
-            glucoseEntry.setDevice(record.getDeviceName());
-            glucoseEntry.setSgv(record.getSgv());
-            glucoseEntry.setDate(record.getSgvDate().getTime());
-            glucoseEntry.setDateString(record.getSgvDate().toString());
-
-            glucoseEntries.add(glucoseEntry);
+            if (record.isValidSGV()) {
+                SgvEntry sgvEntry = new SgvEntry();
+                sgvEntry.setType("sgv");
+                sgvEntry.setDirection(EntriesSerializer.getDirectionStringStatus(record.getCgmTrend()));
+                sgvEntry.setDevice(record.getDeviceName());
+                sgvEntry.setSgv(record.getSgv());
+                sgvEntry.setDate(record.getCgmDate().getTime());
+                sgvEntry.setDateString(record.getCgmDate().toString());
+                sgvEntries.add(sgvEntry);
+            }
 
-            if (record.getBolusWizardBGL() != 0) {
-                BolusEntry bolusEntry = new BolusEntry();
+            if (record.isValidBGL()) {
+
+                MbgEntry mbgEntry = new MbgEntry();
+                mbgEntry.setType("mbg");
+                mbgEntry.setDate(record.getEventDate().getTime());
+                mbgEntry.setDateString(record.getEventDate().toString());
+                mbgEntry.setDevice(record.getDeviceName());
+                mbgEntry.setMbg(record.getRecentBGL());
+                mbgEntries.add(mbgEntry);
+
+                // cgm offline or not in use (needed for NS to show bgl when no sgv data)
+                if (!record.isCgmActive() || record.isCgmWarmUp()) {
+                    ConfigurationStore configurationStore = ConfigurationStore.getInstance();
+                    BigDecimal bgl;
+                    String units;
+                    if (configurationStore.isMmolxl()) {
+                        bgl = new BigDecimal(record.getRecentBGL() / MMOLXLFACTOR).setScale(1, BigDecimal.ROUND_HALF_UP);
+                        units = "mmol";
+                    } else {
+                        bgl = new BigDecimal(record.getRecentBGL()).setScale(0);
+                        units = "mg/dl";
+                    }
+
+                    TreatmentEntry treatmentEntry = new TreatmentEntry();
+                    treatmentEntry.setCreatedAt(ISO8601_DATE_FORMAT.format(record.getEventDate()));
+                    treatmentEntry.setEventType("BG Check");
+                    treatmentEntry.setGlucoseType("Finger");
+                    treatmentEntry.setGlucose(bgl);
+                    treatmentEntry.setUnits(units);
+                    treatmentEntries.add(treatmentEntry);
+                }
+            }
 
-                bolusEntry.setType("mbg");
-                bolusEntry.setDate(record.getEventDate().getTime());
-                bolusEntry.setDateString(record.getEventDate().toString());
-                bolusEntry.setDevice(record.getDeviceName());
-                bolusEntry.setMbg(record.getBolusWizardBGL());
+            if (treatments) {
+
+                if (record.isValidBolus()) {
+
+                    if (record.isValidBolusDual()) {
+                        TreatmentEntry treatmentEntry = new TreatmentEntry();
+                        treatmentEntry.setCreatedAt(ISO8601_DATE_FORMAT.format(record.getLastBolusDate()));
+                        treatmentEntry.setEventType("Bolus");
+                        treatmentEntry.setInsulin(record.getLastBolusAmount());
+                        treatmentEntry.setNotes("Dual bolus normal part delivered: " + record.getLastBolusAmount() + "u");
+                        treatmentEntries.add(treatmentEntry);
+
+                    } else if (record.isValidBolusSquare()) {
+                        TreatmentEntry treatmentEntry = new TreatmentEntry();
+                        treatmentEntry.setCreatedAt(ISO8601_DATE_FORMAT.format(record.getLastBolusDate()));
+                        treatmentEntry.setEventType("Combo Bolus");
+                        treatmentEntry.setDuration(record.getLastBolusDuration());
+                        treatmentEntry.setSplitNow("0");
+                        treatmentEntry.setSplitExt("100");
+                        treatmentEntry.setRelative(2);
+                        treatmentEntry.setEnteredinsulin(String.valueOf(record.getLastBolusAmount()));
+                        treatmentEntries.add(treatmentEntry);
+
+                        noteEntries.add(new NoteEntry(
+                                "Announcement",
+                                ISO8601_DATE_FORMAT.format(record.getLastBolusDate().getTime() + (record.getLastBolusDuration() * 60 * 1000)),
+                                "Square bolus delivered: " + record.getLastBolusAmount() + "u Duration: " + record.getLastBolusDuration() + " minutes"
+                        ));
+
+                    } else {
+                        TreatmentEntry treatmentEntry = new TreatmentEntry();
+                        treatmentEntry.setCreatedAt(ISO8601_DATE_FORMAT.format(record.getLastBolusDate()));
+                        treatmentEntry.setEventType("Bolus");
+                        treatmentEntry.setInsulin(record.getLastBolusAmount());
+                        treatmentEntries.add(treatmentEntry);
+                    }
+                }
+
+                if (record.isValidTEMPBASAL()) {
+                    if (record.getTempBasalMinutesRemaining() > 0 && record.getTempBasalPercentage() > 0) {
+                        tempBasalPercentEntries.add(new TempBasalPercentEntry(
+                                ISO8601_DATE_FORMAT.format(record.getEventDate()),
+                                "Temp Basal started approx: " + NOTE_DATE_FORMAT.format(record.getTempBasalAfterDate()) + " - " + NOTE_DATE_FORMAT.format(record.getTempBasalBeforeDate()),
+                                record.getTempBasalMinutesRemaining(),
+                                record.getTempBasalPercentage() - 100
+                        ));
+                    } else if (record.getTempBasalMinutesRemaining() > 0) {
+                        tempBasalRateEntries.add(new TempBasalRateEntry(
+                                ISO8601_DATE_FORMAT.format(record.getEventDate()),
+                                "Temp Basal started approx: " + NOTE_DATE_FORMAT.format(record.getTempBasalAfterDate()) + " - " + NOTE_DATE_FORMAT.format(record.getTempBasalBeforeDate()),
+                                record.getTempBasalMinutesRemaining(),
+                                record.getTempBasalRate()
+                        ));
+                    } else {
+                        tempBasalCancelEntries.add(new TempBasalCancelEntry(
+                                ISO8601_DATE_FORMAT.format(record.getEventDate()),
+                                "Temp Basal stopped approx: " + NOTE_DATE_FORMAT.format(record.getTempBasalAfterDate()) + " - " + NOTE_DATE_FORMAT.format(record.getTempBasalBeforeDate())
+                        ));
+                    }
+                }
+
+                if (record.isValidSUSPEND()) {
+                    tempBasalRateEntries.add(new TempBasalRateEntry(
+                            ISO8601_DATE_FORMAT.format(record.getEventDate()),
+                            "Pump suspended insulin delivery approx: " + NOTE_DATE_FORMAT.format(record.getSuspendAfterDate()) + " - " + NOTE_DATE_FORMAT.format(record.getSuspendBeforeDate()),
+                            60,
+                            0
+                    ));
+                }
+                if (record.isValidSUSPENDOFF()) {
+                    tempBasalCancelEntries.add(new TempBasalCancelEntry(
+                            ISO8601_DATE_FORMAT.format(record.getEventDate()),
+                            "Pump resumed insulin delivery approx: " + NOTE_DATE_FORMAT.format(record.getSuspendAfterDate()) + " - " + NOTE_DATE_FORMAT.format(record.getSuspendBeforeDate())
+                    ));
+                }
+
+                if (record.isValidSAGE()) {
+                    noteEntries.add(new NoteEntry(
+                            "Sensor Start",
+                            ISO8601_DATE_FORMAT.format(record.getSageAfterDate().getTime() - (record.getSageAfterDate().getTime() - record.getSageBeforeDate().getTime()) / 2),
+                            "Sensor changed approx: " + NOTE_DATE_FORMAT.format(record.getSageAfterDate()) + " - " + NOTE_DATE_FORMAT.format(record.getSageBeforeDate())
+                    ));
+                }
+                if (record.isValidCAGE()) {
+                    noteEntries.add(new NoteEntry(
+                            "Site Change",
+                            ISO8601_DATE_FORMAT.format(record.getCageAfterDate().getTime() - (record.getCageAfterDate().getTime() - record.getCageBeforeDate().getTime()) / 2),
+                            "Reservoir changed approx: " + NOTE_DATE_FORMAT.format(record.getCageAfterDate()) + " - " + NOTE_DATE_FORMAT.format(record.getCageBeforeDate())
+                    ));
+                }
+                if (record.isValidBATTERY()) {
+                    noteEntries.add(new NoteEntry(
+                            "Note",
+                            ISO8601_DATE_FORMAT.format(record.getBatteryAfterDate().getTime() - (record.getBatteryAfterDate().getTime() - record.getBatteryBeforeDate().getTime()) / 2),
+                            "Pump battery changed approx: " + NOTE_DATE_FORMAT.format(record.getBatteryAfterDate()) + " - " + NOTE_DATE_FORMAT.format(record.getBatteryBeforeDate())
+                    ));
+                }
 
-                bolusEntries.add(bolusEntry);
             }
 
         }
 
         boolean uploaded = true;
-        if (glucoseEntries.size() > 0) {
-            Response<ResponseBody> result = glucoseEndpoints.sendEntries(glucoseEntries).execute();
+        if (sgvEntries.size() > 0) {
+            Response<ResponseBody> result = sgvEndpoints.sendEntries(sgvEntries).execute();
             uploaded = result.isSuccessful();
         }
-        if (bolusEntries.size() > 0) {
-            Response<ResponseBody> result = bolusEndpoints.sendEntries(bolusEntries).execute();
+        if (mbgEntries.size() > 0) {
+            Response<ResponseBody> result = mbgEndpoints.sendEntries(mbgEntries).execute();
+            uploaded = uploaded && result.isSuccessful();
+        }
+        if (treatmentEntries.size() > 0) {
+            Response<ResponseBody> result = treatmentEndpoints.sendEntries(treatmentEntries).execute();
+            uploaded = uploaded && result.isSuccessful();
+        }
+        if (tempBasalRateEntries.size() > 0) {
+            Response<ResponseBody> result = tempBasalRateEndpoints.sendEntries(tempBasalRateEntries).execute();
+            uploaded = uploaded && result.isSuccessful();
+        }
+        if (tempBasalPercentEntries.size() > 0) {
+            Response<ResponseBody> result = tempBasalPercentEndpoints.sendEntries(tempBasalPercentEntries).execute();
+            uploaded = uploaded && result.isSuccessful();
+        }
+        if (tempBasalCancelEntries.size() > 0) {
+            Response<ResponseBody> result = tempBasalCancelEndpoints.sendEntries(tempBasalCancelEntries).execute();
+            uploaded = uploaded && result.isSuccessful();
+        }
+        if (noteEntries.size() > 0) {
+            Response<ResponseBody> result = noteEndpoints.sendEntries(noteEntries).execute();
             uploaded = uploaded && result.isSuccessful();
         }
         return uploaded;
@@ -125,21 +290,89 @@ class NightScoutUpload {
         List<DeviceStatus> deviceEntries = new ArrayList<>();
         for (PumpStatusEvent record : records) {
 
-            Iob iob = new Iob(record.getPumpDate(), record.getActiveInsulin());
+            Iob iob = new Iob(record.getEventDate(), record.getActiveInsulin());
             Battery battery = new Battery(record.getBatteryPercentage());
             PumpStatus pumpstatus;
-            if (record.isBolusing()) {
-                pumpstatus = new PumpStatus(true, false, "");
 
+            // shorten pump status when needed to accommodate mobile browsers
+            boolean shorten = false;
+            if ((record.isBolusingSquare() || record.isBolusingDual()) && record.isTempBasalActive())
+                shorten = true;
+
+            String statusPUMP = "normal";
+            if (record.isBolusingNormal()) {
+                statusPUMP = "bolusing";
             } else if (record.isSuspended()) {
-                pumpstatus = new PumpStatus(false, true, "");
+                statusPUMP = "suspended";
+            } else {
+                if (record.isBolusingSquare()) {
+                    if (shorten)
+                        statusPUMP = "S>" + record.getBolusingDelivered() + "u-" + record.getBolusingMinutesRemaining() + "m";
+                    else
+                        statusPUMP = "square>>" + record.getBolusingDelivered() + "u-" + (record.getBolusingMinutesRemaining() >= 60 ? record.getBolusingMinutesRemaining() / 60 + "h" : "") + record.getBolusingMinutesRemaining() % 60 + "m";
+                    shorten = true;
+                } else if (record.isBolusingDual()) {
+                    if (shorten)
+                        statusPUMP = "D>" + record.getBolusingDelivered() + "-" + record.getBolusingMinutesRemaining() + "m";
+                    else
+                        statusPUMP = "dual>>" + record.getBolusingDelivered() + "u-" + (record.getBolusingMinutesRemaining() >= 60 ? record.getBolusingMinutesRemaining() / 60 + "h" : "") + record.getBolusingMinutesRemaining() % 60 + "m";
+                    shorten = true;
+                }
+                if (record.getTempBasalMinutesRemaining() > 0 & record.getTempBasalPercentage() != 0) {
+                    if (shorten)
+                        statusPUMP = " T>" + record.getTempBasalPercentage() + "%-" + record.getTempBasalMinutesRemaining() + "m";
+                    else
+                        statusPUMP = "temp>>" + record.getTempBasalPercentage() + "%-" + (record.getTempBasalMinutesRemaining() >= 60 ? record.getTempBasalMinutesRemaining() / 60 + "h" : "") + record.getTempBasalMinutesRemaining() % 60 + "m";
+                    shorten = true;
+                } else if (record.getTempBasalMinutesRemaining() > 0) {
+                    if (shorten)
+                        statusPUMP = " T>" + record.getTempBasalRate() + "-" + record.getTempBasalMinutesRemaining() + "m";
+                    else
+                        statusPUMP = "temp>>" + record.getTempBasalRate() + "u-" + (record.getTempBasalMinutesRemaining() >= 60 ? record.getTempBasalMinutesRemaining() / 60 + "h" : "") + record.getTempBasalMinutesRemaining() % 60 + "m";
+                    shorten = true;
+                }
+            }
+
+            if (record.getAlert() > 0)
+                statusPUMP = "⚠ " + statusPUMP;
+
+            String statusCGM = "";
+            if (record.isCgmActive()) {
+                if (record.getTransmitterBattery() > 80)
+                    statusCGM = shorten ? "" : ":::: ";
+                else if (record.getTransmitterBattery() > 55)
+                    statusCGM = shorten ? "" : ":::. ";
+                else if (record.getTransmitterBattery() > 30)
+                    statusCGM = shorten ? "" : "::.. ";
+                else if (record.getTransmitterBattery() > 10)
+                    statusCGM = shorten ? "" : ":... ";
+                else
+                    statusCGM = shorten ? "" : ".... ";
+                if (record.isCgmCalibrating())
+                    statusCGM += shorten ? "cal" : "calibrating";
+                else if (record.isCgmCalibrationComplete())
+                    statusCGM += shorten ? "cal" : "cal.complete";
+                else {
+                    if (record.isCgmWarmUp())
+                        statusCGM += shorten ? "WU" : "warmup ";
+                    if (record.getCalibrationDueMinutes() > 0) {
+                        if (shorten)
+                            statusCGM += (record.getCalibrationDueMinutes() >= 120 ? record.getCalibrationDueMinutes() / 60 + "h" : record.getCalibrationDueMinutes() + "m");
+                        else
+                            statusCGM += (record.getCalibrationDueMinutes() >= 60 ? record.getCalibrationDueMinutes() / 60 + "h" : "") + record.getCalibrationDueMinutes() % 60 + "m";
+                    }
+                    else
+                        statusCGM += shorten ? "cal.now" : "calibrate now!";
+                }
             } else {
-                pumpstatus = new PumpStatus(false, false, "normal");
+                statusCGM += shorten ? "n/a" : "cgm n/a";
             }
 
+            pumpstatus = new PumpStatus(false, false, statusPUMP + " " + statusCGM);
+
             PumpInfo pumpInfo = new PumpInfo(
-                    ISO8601_DATE_FORMAT.format(record.getPumpDate()),
-                    new BigDecimal(record.getReservoirAmount()).setScale(3, BigDecimal.ROUND_HALF_UP),
+                    ISO8601_DATE_FORMAT.format(record.getEventDate()),
+                    new BigDecimal(record.getReservoirAmount()).setScale(0, BigDecimal.ROUND_HALF_UP),
                     iob,
                     battery,
                     pumpstatus
@@ -148,7 +381,7 @@ class NightScoutUpload {
             DeviceStatus deviceStatus = new DeviceStatus(
                     uploaderBatteryLevel,
                     record.getDeviceName(),
-                    ISO8601_DATE_FORMAT.format(record.getPumpDate()),
+                    ISO8601_DATE_FORMAT.format(record.getEventDate()),
                     pumpInfo
             );
 
@@ -176,5 +409,4 @@ class NightScoutUpload {
         }
         return sb.toString();
     }
-
 }
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 038218934c13ccb0c2e99389977a5106ec8b536a..30dcd7cdc73e1c3bddebe863d3af2c5201ade573 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
@@ -11,6 +11,7 @@ import android.support.v4.content.LocalBroadcastManager;
 import android.util.Log;
 
 import info.nightscout.android.R;
+import info.nightscout.android.medtronic.service.MedtronicCnlIntentService;
 import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
 import info.nightscout.android.utils.DataStore;
 import io.realm.Realm;
@@ -29,8 +30,8 @@ public class NightscoutUploadIntentService extends IntentService {
 
     protected void sendStatus(String message) {
         Intent localIntent =
-                new Intent(Constants.ACTION_STATUS_MESSAGE)
-                        .putExtra(Constants.EXTENDED_DATA, message);
+                new Intent(MedtronicCnlIntentService.Constants.ACTION_STATUS_MESSAGE)
+                        .putExtra(MedtronicCnlIntentService.Constants.EXTENDED_DATA, message);
         LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
     }
 
@@ -53,13 +54,14 @@ public class NightscoutUploadIntentService extends IntentService {
         RealmResults<PumpStatusEvent> records = mRealm
                 .where(PumpStatusEvent.class)
                 .equalTo("uploaded", false)
-                .notEqualTo("sgv", 0)
                 .findAll();
 
         if (records.size() > 0) {
             SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
 
             Boolean enableRESTUpload = prefs.getBoolean("EnableRESTUpload", false);
+            Boolean enableTreatmentsUpload = prefs.getBoolean("EnableTreatmentsUpload", false);
+
             try {
                 if (enableRESTUpload) {
                     long start = System.currentTimeMillis();
@@ -67,21 +69,30 @@ public class NightscoutUploadIntentService extends IntentService {
                     String urlSetting = prefs.getString(mContext.getString(R.string.preference_nightscout_url), "");
                     String secretSetting = prefs.getString(mContext.getString(R.string.preference_api_secret), "YOURAPISECRET");
                     Boolean uploadSuccess = mNightScoutUpload.doRESTUpload(urlSetting,
-                            secretSetting, DataStore.getInstance().getUploaderBatteryLevel(), records);
+                            secretSetting, enableTreatmentsUpload, DataStore.getInstance().getUploaderBatteryLevel(), records);
                     if (uploadSuccess) {
                         mRealm.beginTransaction();
                         for (PumpStatusEvent updateRecord : records) {
                             updateRecord.setUploaded(true);
                         }
                         mRealm.commitTransaction();
+                    } else {
+                        sendStatus(MedtronicCnlIntentService.ICON_WARN + "Uploading to Nightscout returned unsuccessful");
                     }
                     Log.i(TAG, String.format("Finished upload of %s record using a REST API in %s ms", records.size(), System.currentTimeMillis() - start));
+                } else {
+                    mRealm.beginTransaction();
+                    for (PumpStatusEvent updateRecord : records) {
+                        updateRecord.setUploaded(true);
+                    }
+                    mRealm.commitTransaction();
                 }
             } catch (Exception e) {
+                sendStatus(MedtronicCnlIntentService.ICON_WARN + "Error uploading: " + e.getMessage());
                 Log.e(TAG, "ERROR uploading data!!!!!", e);
             }
         } else {
-            Log.i(TAG, "No records has to be uploaded");
+            Log.i(TAG, "No records have to be uploaded");
         }
         mRealm.close();
 
@@ -100,5 +111,4 @@ public class NightscoutUploadIntentService extends IntentService {
         public static final String EXTENDED_DATA = "info.nightscout.android.upload.nightscout.DATA";
     }
 
-
 }
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 1f40ab015abd146de2c596efb414ad798df58594..0b44575a4313565608b2c17d21e39ad5561ac568 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
@@ -80,8 +80,8 @@ public class EntriesSerializer implements JsonSerializer<PumpStatusEvent> {
         jsonObject.addProperty("direction", getDirectionString(src.getCgmTrend()));
         jsonObject.addProperty("device", src.getDeviceName());
         jsonObject.addProperty("type", "sgv");
-        jsonObject.addProperty("date", src.getSgvDate().getTime());
-        jsonObject.addProperty("dateString", String.valueOf(src.getSgvDate()));
+        jsonObject.addProperty("date", src.getCgmDate().getTime());
+        jsonObject.addProperty("dateString", String.valueOf(src.getCgmDate()));
 
         return jsonObject;
     }
diff --git a/app/src/main/java/info/nightscout/android/utils/DataStore.java b/app/src/main/java/info/nightscout/android/utils/DataStore.java
index 86bfa944f893a0fa8925c7c0da7d6c7a00a2b73c..c6f24d69bfb4a71d813d6e54400edc5e92f9277a 100644
--- a/app/src/main/java/info/nightscout/android/utils/DataStore.java
+++ b/app/src/main/java/info/nightscout/android/utils/DataStore.java
@@ -1,13 +1,8 @@
 package info.nightscout.android.utils;
 
 
-import org.apache.commons.lang3.time.DateUtils;
-
 import java.util.Date;
 
-import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
-import io.realm.Realm;
-
 /**
  * Created by volker on 30.03.2017.
  */
@@ -15,16 +10,21 @@ import io.realm.Realm;
 public class DataStore {
     private static DataStore instance;
 
-    private PumpStatusEvent lastPumpStatus;
-    private int uploaderBatteryLevel = 0;
-    private int unavailableSGVCount = 0;
-    private int lastBolusWizardBGL = 0;
+    private Date uploaderStartDate;
+
     private long activePumpMac = 0;
-    private int commsErrorCount = 0;
-    private int commsSuccessCount = 0;
-    private int commsConnectThreshold = 0;
-    private int commsSignalThreshold = 0;
-    private float commsUnavailableThreshold = 0;
+    private int uploaderBatteryLevel = 0;
+
+    private int pumpCgmNA = 0;
+
+    private int CommsSuccess = 0;
+    private int CommsError = 0;
+    private int CommsConnectError = 0;
+    private int CommsSignalError = 0;
+    private int CommsSgvSuccess = 0;
+    private int PumpLostSensorError = 0;
+    private int PumpClockError = 0;
+    private int PumpBatteryError = 0;
 
     private DataStore() {}
 
@@ -32,27 +32,21 @@ public class DataStore {
         if (DataStore.instance == null) {
             instance = new DataStore();
 
-            // set some initial dummy values
-            PumpStatusEvent dummyStatus = new PumpStatusEvent();
-            dummyStatus.setSgvDate(DateUtils.addDays(new Date(), -1));
-            dummyStatus.setSgv(0);
-
-            // bypass setter to avoid dealing with a real Realm object
-            instance.lastPumpStatus = dummyStatus;
+            instance.uploaderStartDate = new Date();
         }
-
         return instance;
     }
 
-    public PumpStatusEvent getLastPumpStatus() {
-        return lastPumpStatus;
+    public Date getUploaderStartDate() {
+        return uploaderStartDate;
     }
 
-    public void setLastPumpStatus(PumpStatusEvent lastPumpStatus) {
-        Realm realm = Realm.getDefaultInstance();
+    public long getActivePumpMac() {
+        return activePumpMac;
+    }
 
-        this.lastPumpStatus = realm.copyFromRealm(lastPumpStatus);
-        if (!realm.isClosed()) realm.close();
+    public void setActivePumpMac(long activePumpMac) {
+        this.activePumpMac = activePumpMac;
     }
 
     public int getUploaderBatteryLevel() {
@@ -63,112 +57,109 @@ public class DataStore {
         this.uploaderBatteryLevel = uploaderBatteryLevel;
     }
 
-    public int getUnavailableSGVCount() {
-        return unavailableSGVCount;
+    public int getPumpCgmNA() {
+        return pumpCgmNA;
     }
 
-    public int incUnavailableSGVCount() {
-        return unavailableSGVCount++;
+    public int incPumpCgmNA() {
+        return pumpCgmNA++;
     }
 
-    public void clearUnavailableSGVCount() {
-        this.unavailableSGVCount = 0;
+    public void clearPumpCgmNA() {
+        this.pumpCgmNA = 0;
     }
 
-    public void setUnavailableSGVCount(int unavailableSGVCount) {
-        this.unavailableSGVCount = unavailableSGVCount;
+    public int getCommsSuccess() {
+        return CommsSuccess;
     }
 
-    public int getLastBolusWizardBGL() {
-        return lastBolusWizardBGL;
-    }
+    public int incCommsSuccess() { return CommsSuccess++; }
 
-    public void setLastBolusWizardBGL(int lastBolusWizardBGL) {
-        this.lastBolusWizardBGL = lastBolusWizardBGL;
+    public void clearCommsSuccess() {
+        this.CommsSuccess = 0;
     }
 
-    public long getActivePumpMac() {
-        return activePumpMac;
+    public int getCommsError() {
+        return CommsError;
     }
 
-    public void setActivePumpMac(long activePumpMac) {
-        this.activePumpMac = activePumpMac;
+    public int incCommsError() { return CommsError++; }
+
+    public void clearCommsError() {
+        this.CommsError = 0;
     }
 
-    public int getCommsErrorCount() {
-        return commsErrorCount;
+    public int getCommsConnectError() {
+        return CommsConnectError;
     }
 
-    public int incCommsErrorCount() { return commsErrorCount++; }
+    public int incCommsConnectError() { return CommsConnectError++; }
 
-    public int decCommsErrorThreshold() {
-        if (commsErrorCount > 0) commsErrorCount--;
-        return commsErrorCount;}
+    public int decCommsConnectError() {
+        if (CommsConnectError > 0) CommsConnectError--;
+        return CommsConnectError;}
 
-    public void clearCommsErrorCount() {
-        this.commsErrorCount = 0;
+    public void clearCommsConnectError() {
+        this.CommsConnectError = 0;
     }
 
-    public int getCommsSuccessCount() {
-        return commsSuccessCount;
+    public int getCommsSignalError() {
+        return CommsSignalError;
     }
 
-    public int incCommsSuccessCount() { return commsSuccessCount++; }
+    public int incCommsSignalError() { return CommsSignalError++; }
 
-    public int decCommsSuccessCount() {
-        if (commsSuccessCount > 0) commsSuccessCount--;
-        return commsSuccessCount;}
+    public int decCommsSignalError() {
+        if (CommsSignalError > 0) CommsSignalError--;
+        return CommsSignalError;}
 
-    public void clearCommsSuccessCount() {
-        this.commsSuccessCount = 0;
+    public void clearCommsSignalError() {
+        this.CommsSignalError = 0;
     }
 
-    public int getCommsConnectThreshold() {
-        return commsConnectThreshold;
+    public int getCommsSgvSuccess() {
+        return CommsSgvSuccess;
     }
 
-    public int incCommsConnectThreshold() { return commsConnectThreshold++; }
-
-    public int decCommsConnectThreshold() {
-        if (commsConnectThreshold > 0) commsConnectThreshold--;
-        return commsConnectThreshold;}
+    public int incCommsSgvSuccess() { return CommsSgvSuccess++; }
 
-    public void clearCommsConnectThreshold() {
-        this.commsConnectThreshold = 0;
+    public int getPumpLostSensorError() {
+        return PumpLostSensorError;
     }
 
-    public int getCommsSignalThreshold() {
-        return commsSignalThreshold;
+    public int incPumpLostSensorError() { return PumpLostSensorError++; }
+
+    public void clearPumpLostSensorError() {
+        this.PumpLostSensorError = 0;
     }
 
-    public int incCommsSignalThreshold() { return commsSignalThreshold++; }
+    public int getPumpClockError() {
+        return PumpClockError;
+    }
 
-    public int decCommsSignalThreshold() {
-        if (commsSignalThreshold > 0) commsSignalThreshold--;
-        return commsSignalThreshold;}
+    public int incPumpClockError() { return PumpClockError++; }
 
-    public void clearCommsSignalThreshold() {
-        this.commsSignalThreshold = 0;
+    public void clearPumpClockError() {
+        this.PumpClockError = 0;
     }
 
-    public float getCommsUnavailableThreshold() {
-        return commsUnavailableThreshold;
+    public int getPumpBatteryError() {
+        return PumpBatteryError;
     }
 
-    public float addCommsUnavailableThreshold(float value) {
-        commsUnavailableThreshold+= value;
-        if (commsUnavailableThreshold < 0) commsUnavailableThreshold = 0;
-        return commsUnavailableThreshold;}
+    public int incPumpBatteryError() { return PumpBatteryError++; }
 
-    public void clearCommsUnavailableThreshold() {
-        this.commsUnavailableThreshold = 0;
+    public void clearPumpBatteryError() {
+        this.PumpBatteryError = 0;
     }
 
     public void clearAllCommsErrors() {
-        this.commsErrorCount = 0;
-        this.commsSuccessCount = 0;
-        this.commsConnectThreshold = 0;
-        this.commsSignalThreshold = 0;
-        this.commsUnavailableThreshold = 0;
+        this.CommsSuccess = 0;
+        this.CommsError = 0;
+        this.CommsConnectError = 0;
+        this.CommsSignalError = 0;
+        this.PumpLostSensorError = 0;
+        this.PumpClockError = 0;
+        this.PumpBatteryError = 0;
     }
 }
diff --git a/app/src/main/java/info/nightscout/android/utils/StatusStore.java b/app/src/main/java/info/nightscout/android/utils/StatusStore.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd0e39e4dd553abf4f794c0910a8f7774f064ea8
--- /dev/null
+++ b/app/src/main/java/info/nightscout/android/utils/StatusStore.java
@@ -0,0 +1,27 @@
+package info.nightscout.android.utils;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+import io.realm.RealmObject;
+import io.realm.annotations.Index;
+
+public class StatusStore extends RealmObject {
+    @Index
+    private long timestamp;
+    private String message;
+
+    public void StatusMessage(String message) {
+        StatusMessage(System.currentTimeMillis(), message);
+    }
+
+    public void StatusMessage(long timestamp, String message) {
+        this.timestamp = timestamp;
+        this.message = message;
+    }
+
+    public String toString() {
+        DateFormat df = new SimpleDateFormat("E HH:mm:ss");
+        return df.format(timestamp) + ": " + message;
+    }
+}
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
index e06a88b8cf047dde4d5a812b58198e69bd508573..22be13663c6931536562a3aa7ecb7ee20c4c4089 100644
--- a/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadIntentService.java
+++ b/app/src/main/java/info/nightscout/android/xdrip_plus/XDripPlusUploadIntentService.java
@@ -61,7 +61,9 @@ public class XDripPlusUploadIntentService extends IntentService {
 
         RealmResults<PumpStatusEvent> all_records = mRealm
                 .where(PumpStatusEvent.class)
-                .notEqualTo("sgv", 0)
+                .equalTo("validSGV", true)
+                .or()
+                .equalTo("validBGL", true)
                 .findAllSorted("eventDate", Sort.DESCENDING);
 
         // get the most recent record and send that
@@ -117,14 +119,14 @@ public class XDripPlusUploadIntentService extends IntentService {
         JSONObject json = new JSONObject();
         json.put("uploaderBattery", DataStore.getInstance().getUploaderBatteryLevel());
         json.put("device", record.getDeviceName());
-        json.put("created_at", ISO8601_DATE_FORMAT.format(record.getPumpDate()));
+        json.put("created_at", ISO8601_DATE_FORMAT.format(record.getEventDate()));
 
         JSONObject pumpInfo = new JSONObject();
-        pumpInfo.put("clock", ISO8601_DATE_FORMAT.format(record.getPumpDate()));
+        pumpInfo.put("clock", ISO8601_DATE_FORMAT.format(record.getEventDate()));
         pumpInfo.put("reservoir", new BigDecimal(record.getReservoirAmount()).setScale(3, BigDecimal.ROUND_HALF_UP));
 
         JSONObject iob = new JSONObject();
-        iob.put("timestamp", record.getPumpDate());
+        iob.put("timestamp", record.getEventDate());
         iob.put("bolusiob", record.getActiveInsulin());
 
         JSONObject battery = new JSONObject();
@@ -139,25 +141,27 @@ public class XDripPlusUploadIntentService extends IntentService {
     }
 
     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.getSgvDate().getTime());
-        json.put("dateString", pumpRecord.getSgvDate());
-
-        entriesArray.put(json);
+        if (pumpRecord.isValidSGV()) {
+            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.getCgmDate().getTime());
+            json.put("dateString", pumpRecord.getCgmDate());
+
+            entriesArray.put(json);
+        }
     }
 
     private void addMbgEntry(JSONArray entriesArray, PumpStatusEvent pumpRecord) throws Exception {
-        if (pumpRecord.hasRecentBolusWizard()) {
+        if (pumpRecord.isValidBGL()) {
             JSONObject json = new JSONObject();
 
             // TODO replace with Retrofit/EntriesSerializer
             json.put("type", "mbg");
-            json.put("mbg", pumpRecord.getBolusWizardBGL());
+            json.put("mbg", pumpRecord.getRecentBGL());
             json.put("device", pumpRecord.getDeviceName());
             json.put("date", pumpRecord.getEventDate().getTime());
             json.put("dateString", pumpRecord.getEventDate());
diff --git a/app/src/main/java/info/nightscout/api/BolusEndpoints.java b/app/src/main/java/info/nightscout/api/MbgEndpoints.java
similarity index 63%
rename from app/src/main/java/info/nightscout/api/BolusEndpoints.java
rename to app/src/main/java/info/nightscout/api/MbgEndpoints.java
index a1c2a56e35eacaa534b5e5ede7de4fca577672c8..9c443342275e0b708b7beaef4c8c489375483479 100644
--- a/app/src/main/java/info/nightscout/api/BolusEndpoints.java
+++ b/app/src/main/java/info/nightscout/api/MbgEndpoints.java
@@ -8,57 +8,36 @@ import retrofit2.http.Body;
 import retrofit2.http.Headers;
 import retrofit2.http.POST;
 
-public interface BolusEndpoints {
+public interface MbgEndpoints {
 
-    class BolusEntry {
+    class MbgEntry {
         String type;
         String dateString;
         long date;
         int mbg;
         String device;
 
-        public BolusEntry() {  }
-
-        public String getType() {
-            return type;
-        }
+        public MbgEntry() {  }
 
         public void setType(String type) {
             this.type = type;
         }
 
-        public String getDateString() {
-            return dateString;
-        }
-
         public void setDateString(String dateString) {
             this.dateString = dateString;
         }
 
-        public long getDate() {
-            return date;
-        }
-
         public void setDate(long date) {
             this.date = date;
         }
 
-        public int getMbg() {
-            return mbg;
-        }
-
         public void setMbg(int mbg) {
             this.mbg = mbg;
         }
 
-        public String getDevice() {
-            return device;
-        }
-
         public void setDevice(String device) {
             this.device = device;
         }
-
     }
 
     @Headers({
@@ -66,8 +45,7 @@ public interface BolusEndpoints {
             "Content-type: application/json"
     })
     @POST("/api/v1/entries")
-    Call<ResponseBody> sendEntries(@Body List<BolusEntry> entries);
-
+    Call<ResponseBody> sendEntries(@Body List<MbgEntry> entries);
 }
 
 
diff --git a/app/src/main/java/info/nightscout/api/NoteEndpoints.java b/app/src/main/java/info/nightscout/api/NoteEndpoints.java
new file mode 100644
index 0000000000000000000000000000000000000000..cd51af7b265ff20902c7c09b8d007e7c67c26836
--- /dev/null
+++ b/app/src/main/java/info/nightscout/api/NoteEndpoints.java
@@ -0,0 +1,31 @@
+package info.nightscout.api;
+
+import java.util.List;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.http.Body;
+import retrofit2.http.Headers;
+import retrofit2.http.POST;
+
+public interface NoteEndpoints {
+
+    class NoteEntry {
+        String eventType;
+        String created_at;
+        String notes;
+
+        public NoteEntry(String eventType, String created_at, String notes) {
+            this.eventType = eventType;
+            this.created_at = created_at;
+            this.notes = notes;
+        }
+    }
+
+    @Headers({
+            "Accept: application/json",
+            "Content-type: application/json"
+    })
+    @POST("/api/v1/treatments")
+    Call<ResponseBody> sendEntries(@Body List<NoteEntry> noteEntries);
+}
diff --git a/app/src/main/java/info/nightscout/api/GlucoseEndpoints.java b/app/src/main/java/info/nightscout/api/SgvEndpoints.java
similarity index 62%
rename from app/src/main/java/info/nightscout/api/GlucoseEndpoints.java
rename to app/src/main/java/info/nightscout/api/SgvEndpoints.java
index d34c5e7abdae5efadc346bc560eb54aee252dacb..96dd7bb376d5e73ad325bea640d134ab66144ce5 100644
--- a/app/src/main/java/info/nightscout/api/GlucoseEndpoints.java
+++ b/app/src/main/java/info/nightscout/api/SgvEndpoints.java
@@ -8,10 +8,9 @@ import retrofit2.http.Body;
 import retrofit2.http.Headers;
 import retrofit2.http.POST;
 
-public interface GlucoseEndpoints {
-
-    class GlucoseEntry {
+public interface SgvEndpoints {
 
+    class SgvEntry {
         String type;
         String dateString;
         long date;
@@ -19,55 +18,31 @@ public interface GlucoseEndpoints {
         String direction;
         String device;
 
-        public String getType() {
-            return type;
-        }
-
         public void setType(String type) {
             this.type = type;
         }
 
-        public String getDateString() {
-            return dateString;
-        }
-
         public void setDateString(String dateString) {
             this.dateString = dateString;
         }
 
-        public long getDate() {
-            return date;
-        }
-
         public void setDate(long date) {
             this.date = date;
         }
 
-        public int getSgv() {
-            return sgv;
-        }
-
         public void setSgv(int sgv) {
             this.sgv = sgv;
         }
 
-        public String getDirection() {
-            return direction;
-        }
-
         public void setDirection(String direction) {
             this.direction = direction;
         }
 
-        public String getDevice() {
-            return device;
-        }
-
         public void setDevice(String device) {
             this.device = device;
         }
 
-        public GlucoseEntry() {  }
+        public SgvEntry() {  }
     }
 
     @Headers({
@@ -75,8 +50,7 @@ public interface GlucoseEndpoints {
             "Content-type: application/json"
     })
     @POST("/api/v1/entries")
-    Call<ResponseBody> sendEntries(@Body List<GlucoseEntry> entries);
-
+    Call<ResponseBody> sendEntries(@Body List<SgvEntry> entries);
 }
 
 
diff --git a/app/src/main/java/info/nightscout/api/TempBasalCancelEndpoints.java b/app/src/main/java/info/nightscout/api/TempBasalCancelEndpoints.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d0ca98de5268200421ccd369f45f439966bb2cc
--- /dev/null
+++ b/app/src/main/java/info/nightscout/api/TempBasalCancelEndpoints.java
@@ -0,0 +1,31 @@
+package info.nightscout.api;
+
+import java.util.List;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.http.Body;
+import retrofit2.http.Headers;
+import retrofit2.http.POST;
+
+public interface TempBasalCancelEndpoints {
+
+    class TempBasalCancelEntry {
+        String eventType = "Temp Basal";
+        String created_at;
+        String notes;
+        float duration = 0;
+
+        public TempBasalCancelEntry(String created_at, String notes) {
+            this.created_at = created_at;
+            this.notes = notes;
+        }
+    }
+
+    @Headers({
+            "Accept: application/json",
+            "Content-type: application/json"
+    })
+    @POST("/api/v1/treatments")
+    Call<ResponseBody> sendEntries(@Body List<TempBasalCancelEntry> entries);
+}
diff --git a/app/src/main/java/info/nightscout/api/TempBasalPercentEndpoints.java b/app/src/main/java/info/nightscout/api/TempBasalPercentEndpoints.java
new file mode 100644
index 0000000000000000000000000000000000000000..2622b3c1804c8a512c90cf7eaca74cf607d72b16
--- /dev/null
+++ b/app/src/main/java/info/nightscout/api/TempBasalPercentEndpoints.java
@@ -0,0 +1,34 @@
+package info.nightscout.api;
+
+import java.util.List;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.http.Body;
+import retrofit2.http.Headers;
+import retrofit2.http.POST;
+
+public interface TempBasalPercentEndpoints {
+
+    class TempBasalPercentEntry {
+        String eventType = "Temp Basal";
+        String created_at;
+        String notes;
+        float duration;
+        float percent;
+
+        public TempBasalPercentEntry(String created_at, String notes, float duration, float percent) {
+            this.created_at = created_at;
+            this.notes = notes;
+            this.duration = duration;
+            this.percent = percent;
+        }
+    }
+
+    @Headers({
+            "Accept: application/json",
+            "Content-type: application/json"
+    })
+    @POST("/api/v1/treatments")
+    Call<ResponseBody> sendEntries(@Body List<TempBasalPercentEntry> entries);
+}
diff --git a/app/src/main/java/info/nightscout/api/TempBasalRateEndpoints.java b/app/src/main/java/info/nightscout/api/TempBasalRateEndpoints.java
new file mode 100644
index 0000000000000000000000000000000000000000..27f7d4ff9fbd7e91d4502ead0e14ebe93e494d5b
--- /dev/null
+++ b/app/src/main/java/info/nightscout/api/TempBasalRateEndpoints.java
@@ -0,0 +1,34 @@
+package info.nightscout.api;
+
+import java.util.List;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.http.Body;
+import retrofit2.http.Headers;
+import retrofit2.http.POST;
+
+public interface TempBasalRateEndpoints {
+
+    class TempBasalRateEntry {
+        String eventType = "Temp Basal";
+        String created_at;
+        String notes;
+        float duration;
+        float absolute;
+
+        public TempBasalRateEntry(String created_at, String notes, float duration, float absolute) {
+            this.created_at = created_at;
+            this.notes = notes;
+            this.duration = duration;
+            this.absolute = absolute;
+        }
+    }
+
+    @Headers({
+            "Accept: application/json",
+            "Content-type: application/json"
+    })
+    @POST("/api/v1/treatments")
+    Call<ResponseBody> sendEntries(@Body List<TempBasalRateEntry> entries);
+}
diff --git a/app/src/main/java/info/nightscout/api/TreatmentEndpoints.java b/app/src/main/java/info/nightscout/api/TreatmentEndpoints.java
new file mode 100644
index 0000000000000000000000000000000000000000..129af210241ddda4230f8401b41647a9ed4d69bf
--- /dev/null
+++ b/app/src/main/java/info/nightscout/api/TreatmentEndpoints.java
@@ -0,0 +1,88 @@
+package info.nightscout.api;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.http.Body;
+import retrofit2.http.Headers;
+import retrofit2.http.POST;
+
+public interface TreatmentEndpoints {
+
+    class TreatmentEntry {
+        String eventType;
+        String created_at;
+        String enteredinsulin;
+        String splitNow;
+        String splitExt;
+        String units;
+        String glucoseType;
+        String notes;
+        String device;
+        float insulin;
+        float duration;
+        float relative;
+        BigDecimal glucose;
+
+        public void setEventType(String eventType) {
+            this.eventType = eventType;
+        }
+
+        public void setCreatedAt(String created_at) {
+            this.created_at = created_at;
+        }
+
+        public void setDevice(String device) {
+            this.device = device;
+        }
+
+        public void setInsulin(float insulin) {
+            this.insulin = insulin;
+        }
+
+        public void setGlucose(BigDecimal glucose) {
+            this.glucose = glucose;
+        }
+
+        public void setDuration(float duration) {
+            this.duration = duration;
+        }
+
+        public void setRelative(float relative) {
+            this.relative = relative;
+        }
+
+        public void setEnteredinsulin(String enteredinsulin) {
+            this.enteredinsulin = enteredinsulin;
+        }
+
+        public void setSplitNow(String splitNow) {
+            this.splitNow = splitNow;
+        }
+
+        public void setSplitExt(String splitExt) {
+            this.splitExt = splitExt;
+        }
+
+        public void setUnits(String units) {
+            this.units = units;
+        }
+
+        public void setGlucoseType(String glucoseType) {
+            this.glucoseType = glucoseType;
+        }
+
+        public void setNotes(String notes) {
+            this.notes = notes;
+        }
+    }
+
+    @Headers({
+            "Accept: application/json",
+            "Content-type: application/json"
+    })
+    @POST("/api/v1/treatments")
+    Call<ResponseBody> sendEntries(@Body List <TreatmentEntry> entries);
+}
diff --git a/app/src/main/java/info/nightscout/api/UploadApi.java b/app/src/main/java/info/nightscout/api/UploadApi.java
index a2c4a1a322a6c897c2e49b40b52f1ab52ee536a5..70ce741c202ab545cbbd805b2fa429dab1887551 100644
--- a/app/src/main/java/info/nightscout/api/UploadApi.java
+++ b/app/src/main/java/info/nightscout/api/UploadApi.java
@@ -14,21 +14,39 @@ import retrofit2.converter.gson.GsonConverterFactory;
 
 public class UploadApi {
     private Retrofit retrofit;
-    private GlucoseEndpoints glucoseEndpoints;
-    private BolusEndpoints bolusEndpoints;
+    private SgvEndpoints sgvEndpoints;
+    private MbgEndpoints mbgEndpoints;
     private DeviceEndpoints deviceEndpoints;
-
-    public GlucoseEndpoints getGlucoseEndpoints() {
-        return glucoseEndpoints;
+    private TreatmentEndpoints treatmentEndpoints;
+    private TempBasalRateEndpoints tempBasalRateEndpoints;
+    private TempBasalPercentEndpoints tempBasalPercentEndpoints;
+    private TempBasalCancelEndpoints tempBasalCancelEndpoints;
+    private NoteEndpoints noteEndpoints;
+
+    public SgvEndpoints getSgvEndpoints() {
+        return sgvEndpoints;
     }
-
-    public BolusEndpoints getBolusEndpoints() {
-        return bolusEndpoints;
+    public MbgEndpoints getMbgEndpoints() {
+        return mbgEndpoints;
     }
-
     public DeviceEndpoints getDeviceEndpoints() {
         return deviceEndpoints;
     }
+    public TreatmentEndpoints getTreatmentEndpoints() {
+        return treatmentEndpoints;
+    }
+    public TempBasalRateEndpoints getTempBasalRateEndpoints() {
+        return tempBasalRateEndpoints;
+    }
+    public TempBasalPercentEndpoints getTempBasalPercentEndpoints() {
+        return tempBasalPercentEndpoints;
+    }
+    public TempBasalCancelEndpoints getTempBasalCancelEndpoints() {
+        return tempBasalCancelEndpoints;
+    }
+    public NoteEndpoints getNoteEndpoints() {
+        return noteEndpoints;
+    }
 
     public UploadApi(String baseURL, String token) {
 
@@ -70,8 +88,13 @@ public class UploadApi {
                 .addConverterFactory(GsonConverterFactory.create())
                 .build();
 
-        glucoseEndpoints = retrofit.create(GlucoseEndpoints.class);
-        bolusEndpoints = retrofit.create(BolusEndpoints.class);
+        sgvEndpoints = retrofit.create(SgvEndpoints.class);
+        mbgEndpoints = retrofit.create(MbgEndpoints.class);
         deviceEndpoints = retrofit.create(DeviceEndpoints.class);
+        treatmentEndpoints = retrofit.create(TreatmentEndpoints.class);
+        tempBasalRateEndpoints = retrofit.create(TempBasalRateEndpoints.class);
+        tempBasalPercentEndpoints = retrofit.create(TempBasalPercentEndpoints.class);
+        tempBasalCancelEndpoints = retrofit.create(TempBasalCancelEndpoints.class);
+        noteEndpoints = retrofit.create(NoteEndpoints.class);
     }
 }
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 8d2fad1e9ffb117584baa409ab72db8c361d45df..6e9a1e7becfca0a268a7e11f0e3da233ade109a4 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -66,7 +66,7 @@
                 android:gravity="bottom|center_horizontal"
                 android:orientation="vertical">
 
-                <TextView
+                <com.mikepenz.iconics.view.IconicsTextView
                     android:id="@+id/textview_trend"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
@@ -135,14 +135,56 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content">
 
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="-10sp"
+                android:layout_gravity="center_horizontal"
+                android:orientation="horizontal">
+
+                <Button
+                    android:id="@+id/button_log_top"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="older log entries" />
+
+                <Button
+                    android:id="@+id/button_log_top_recent"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="most recent" />
+            </LinearLayout>
+
             <com.mikepenz.iconics.view.IconicsTextView
                 android:id="@+id/textview_log"
                 android:layout_width="fill_parent"
                 android:layout_height="wrap_content"
                 android:layout_margin="10sp"
-                android:maxLines="800"
                 android:gravity="bottom"
-                android:text="" />
+                android:text=""
+                android:layout_weight="0.33" />
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="-10sp"
+                android:layout_gravity="center_horizontal"
+                android:orientation="horizontal">
+
+                <Button
+                    android:id="@+id/button_log_bottom"
+                    android:layout_gravity="center_horizontal"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="newer log entries" />
+
+                <Button
+                    android:id="@+id/button_log_bottom_recent"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="most recent" />
+            </LinearLayout>
+
         </LinearLayout>
     </ScrollView>
 
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index ff89a6f2bb0e9027c0f4fc857a79595b7a6fe4d5..d590b78082d9dcf96ba6f0d30679d45121be3e77 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -19,12 +19,12 @@
             android:switchTextOn="2"
             android:title="Decimals"/>
         <ListPreference
-            android:key="pollInterval"
             android:defaultValue="300000"
-            android:title="@string/preferences_poll_interval"
-            android:summary="%s"
             android:entries="@array/poll_interval"
-            android:entryValues="@array/poll_interval_millis"/>
+            android:entryValues="@array/poll_interval_millis"
+            android:key="pollInterval"
+            android:summary="%s"
+            android:title="@string/preferences_poll_interval" />
         <info.nightscout.android.utils.CustomSwitchPreference
             android:disableDependentsState="false"
             android:key="doublePollOnPumpAway"
@@ -70,6 +70,10 @@
             android:dialogTitle="Enter your Nightscout API secret"
             android:key="@string/preference_api_secret"
             android:title="API Secret"/>
+        <CheckBoxPreference android:title="Treatments"
+            android:key="EnableTreatmentsUpload"
+            android:dependency="@string/preference_enable_rest_upload"
+            android:summary="Enable upload of treatments to Nightscout"/>
         <Preference android:title="scan NS-Autoconfig QR-Code"
             android:key="scanButton"
             android:dependency="@string/preference_enable_rest_upload"