From 7d3fd839f542ed6ef6fa248cea429014e35286fe Mon Sep 17 00:00:00 2001
From: Volker Richert <v.richert@addmore.de>
Date: Fri, 16 Dec 2016 12:39:18 +0100
Subject: [PATCH] refactoring finished (untested)

---
 .../info/nightscout/android/USB/USBPower.java |   4 +-
 .../android/medtronic/MedtronicCnlReader.java | 310 ++----------------
 .../medtronic/message/BeginEHSMMessage.java   |   9 +-
 ...va => ChannelNegotiateRequestMessage.java} |  23 +-
 .../ChannelNegotiateResponseMessage.java      |  31 ++
 .../ContourNextLinkBinaryRequestMessage.java  |   1 -
 .../ContourNextLinkBinaryResponseMessage.java |   4 -
 .../ContourNextLinkCommandMessage.java        |   1 -
 .../ContourNextLinkCommandResponse.java       |   2 -
 .../message/ContourNextLinkMessage.java       |   4 -
 .../ContourNextLinkMessageHandler.java        |  11 -
 .../ContourNextLinkRequestMessage.java        |   8 -
 .../ContourNextLinkResponseMessage.java       |   8 -
 .../DeviceInfoResponseCommandMessage.java     |   4 -
 .../medtronic/message/EndEHSMMessage.java     |   9 +-
 .../message/MedtronicResponseMessage.java     |   4 +-
 .../OpenConnectionResponseMessage.java        |   3 -
 .../PumpBasalPatternRequestMessage.java       |  12 +-
 .../PumpBasalPatternResponseMessage.java      |  23 +-
 .../message/PumpStatusRequestMessage.java     |  13 +-
 .../message/PumpStatusResponseMessage.java    | 180 +++++++++-
 .../message/PumpTimeRequestMessage.java       |  12 +-
 .../message/PumpTimeResponseMessage.java      |  32 +-
 .../message/ReadInfoRequestMessage.java       |   3 +-
 .../service/MedtronicCnlIntentService.java    |  12 +-
 .../model/medtronicNg/PumpStatusEvent.java    |  23 +-
 .../NightscoutUploadIntentService.java        |   2 -
 27 files changed, 375 insertions(+), 373 deletions(-)
 rename app/src/main/java/info/nightscout/android/medtronic/message/{ChannelNegotiateMessage.java => ChannelNegotiateRequestMessage.java} (50%)
 create mode 100644 app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateResponseMessage.java
 delete mode 100644 app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessageHandler.java

diff --git a/app/src/main/java/info/nightscout/android/USB/USBPower.java b/app/src/main/java/info/nightscout/android/USB/USBPower.java
index 92890b8..cf16a17 100644
--- a/app/src/main/java/info/nightscout/android/USB/USBPower.java
+++ b/app/src/main/java/info/nightscout/android/USB/USBPower.java
@@ -1,9 +1,9 @@
 package info.nightscout.android.USB;
 
-import java.io.DataOutputStream;
-
 import android.util.Log;
 
+import java.io.DataOutputStream;
+
 public class USBPower {
 
     private static final String TAG = "USBPower";
diff --git a/app/src/main/java/info/nightscout/android/medtronic/MedtronicCnlReader.java b/app/src/main/java/info/nightscout/android/medtronic/MedtronicCnlReader.java
index 766a8a6..ca2629e 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/MedtronicCnlReader.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/MedtronicCnlReader.java
@@ -4,11 +4,7 @@ import android.util.Log;
 
 import org.apache.commons.lang3.ArrayUtils;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.math.BigDecimal;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -18,37 +14,33 @@ import java.util.concurrent.TimeoutException;
 
 import info.nightscout.android.USB.UsbHidDriver;
 import info.nightscout.android.medtronic.message.BeginEHSMMessage;
-import info.nightscout.android.medtronic.message.ChannelNegotiateMessage;
+import info.nightscout.android.medtronic.message.ChannelNegotiateRequestMessage;
+import info.nightscout.android.medtronic.message.ChannelNegotiateResponseMessage;
 import info.nightscout.android.medtronic.message.ChecksumException;
-import info.nightscout.android.medtronic.message.ContourNextLinkBinaryMessage;
+import info.nightscout.android.medtronic.message.CloseConnectionRequestMessage;
 import info.nightscout.android.medtronic.message.ContourNextLinkCommandMessage;
-import info.nightscout.android.medtronic.message.ContourNextLinkMessage;
-import info.nightscout.android.medtronic.message.ContourNextLinkMessageHandler;
+import info.nightscout.android.medtronic.message.DeviceInfoRequestCommandMessage;
 import info.nightscout.android.medtronic.message.DeviceInfoResponseCommandMessage;
 import info.nightscout.android.medtronic.message.EncryptionException;
 import info.nightscout.android.medtronic.message.EndEHSMMessage;
-import info.nightscout.android.medtronic.message.MedtronicMessage;
-import info.nightscout.android.medtronic.message.MessageUtils;
+import info.nightscout.android.medtronic.message.OpenConnectionRequestMessage;
 import info.nightscout.android.medtronic.message.PumpBasalPatternRequestMessage;
 import info.nightscout.android.medtronic.message.PumpBasalPatternResponseMessage;
-import info.nightscout.android.medtronic.message.ReadInfoRequestMessage;
 import info.nightscout.android.medtronic.message.PumpStatusRequestMessage;
 import info.nightscout.android.medtronic.message.PumpStatusResponseMessage;
 import info.nightscout.android.medtronic.message.PumpTimeRequestMessage;
 import info.nightscout.android.medtronic.message.PumpTimeResponseMessage;
+import info.nightscout.android.medtronic.message.ReadInfoRequestMessage;
 import info.nightscout.android.medtronic.message.ReadInfoResponseMessage;
 import info.nightscout.android.medtronic.message.RequestLinkKeyRequestMessage;
 import info.nightscout.android.medtronic.message.RequestLinkKeyResponseMessage;
-import info.nightscout.android.medtronic.message.DeviceInfoRequestCommandMessage;
 import info.nightscout.android.medtronic.message.UnexpectedMessageException;
 import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
-import info.nightscout.android.utils.HexDump;
 
 /**
  * Created by lgoedhart on 24/03/2016.
  */
-public class MedtronicCnlReader implements ContourNextLinkMessageHandler {
-
+public class MedtronicCnlReader {
     private static final String TAG = MedtronicCnlReader.class.getSimpleName();
 
     private static final int USB_BLOCKSIZE = 64;
@@ -59,34 +51,12 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler {
     private UsbHidDriver mDevice;
 
     private MedtronicCnlSession mPumpSession = new MedtronicCnlSession();
-
     private String mStickSerial = null;
 
     public MedtronicCnlReader(UsbHidDriver device) {
         mDevice = device;
     }
 
-    private static PumpStatusEvent.CGM_TREND fromMessageByte(byte messageByte) {
-        switch (messageByte) {
-            case (byte) 0x60:
-                return PumpStatusEvent.CGM_TREND.FLAT;
-            case (byte) 0xc0:
-                return PumpStatusEvent.CGM_TREND.DOUBLE_UP;
-            case (byte) 0xa0:
-                return PumpStatusEvent.CGM_TREND.SINGLE_UP;
-            case (byte) 0x80:
-                return PumpStatusEvent.CGM_TREND.FOURTY_FIVE_UP;
-            case (byte) 0x40:
-                return PumpStatusEvent.CGM_TREND.FOURTY_FIVE_DOWN;
-            case (byte) 0x20:
-                return PumpStatusEvent.CGM_TREND.SINGLE_DOWN;
-            case (byte) 0x00:
-                return PumpStatusEvent.CGM_TREND.DOUBLE_DOWN;
-            default:
-                return PumpStatusEvent.CGM_TREND.NOT_COMPUTABLE;
-        }
-    }
-
     public String getStickSerial() {
         return mStickSerial;
     }
@@ -95,75 +65,6 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler {
         return mPumpSession;
     }
 
-    public byte[] readMessage() throws IOException, TimeoutException {
-        ByteArrayOutputStream responseMessage = new ByteArrayOutputStream();
-
-        byte[] responseBuffer = new byte[USB_BLOCKSIZE];
-        int bytesRead;
-        int messageSize = 0;
-
-        do {
-            bytesRead = mDevice.read(responseBuffer, READ_TIMEOUT_MS);
-
-            if (bytesRead == -1) {
-                throw new TimeoutException("Timeout waiting for response from pump");
-            } else if (bytesRead > 0) {
-                // Validate the header
-                ByteBuffer header = ByteBuffer.allocate(3);
-                header.put(responseBuffer, 0, 3);
-                String headerString = new String(header.array());
-                if (!headerString.equals(BAYER_USB_HEADER)) {
-                    throw new IOException("Unexpected header received");
-                }
-                messageSize = responseBuffer[3];
-                responseMessage.write(responseBuffer, 4, messageSize);
-            } else {
-                Log.w(TAG, "readMessage: got a zero-sized response.");
-            }
-        } while (bytesRead > 0 && messageSize == 60);
-
-        String responseString = HexDump.dumpHexString(responseMessage.toByteArray());
-        Log.d(TAG, "READ: " + responseString);
-
-        return responseMessage.toByteArray();
-    }
-
-    @Override
-    public void sendMessage(ContourNextLinkMessage message) throws IOException {
-        sendMessage(message.encode());
-        if (message instanceof ContourNextLinkBinaryMessage) {
-            mPumpSession.incrBayerSequenceNumber();
-        }
-
-        if (message instanceof MedtronicMessage) {
-            mPumpSession.incrMedtronicSequenceNumber();
-        }
-    }
-
-    @Override
-    public ContourNextLinkMessage receiveMessage() {
-        return null;
-    }
-
-    public void sendMessage(byte[] message) throws IOException {
-
-        int pos = 0;
-
-        while (message.length > pos) {
-            ByteBuffer outputBuffer = ByteBuffer.allocate(USB_BLOCKSIZE);
-            int sendLength = (pos + 60 > message.length) ? message.length - pos : 60;
-            outputBuffer.put(BAYER_USB_HEADER.getBytes());
-            outputBuffer.put((byte) sendLength);
-            outputBuffer.put(message, pos, sendLength);
-
-            mDevice.write(outputBuffer.array(), 200);
-            pos += sendLength;
-
-            String outputString = HexDump.dumpHexString(outputBuffer.array());
-            Log.d(TAG, "WRITE: " + outputString);
-        }
-    }
-
     public void requestDeviceInfo()
             throws IOException, TimeoutException, UnexpectedMessageException, ChecksumException, EncryptionException {
         DeviceInfoResponseCommandMessage response = new DeviceInfoRequestCommandMessage(mPumpSession).send(mDevice);
@@ -172,7 +73,6 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler {
         mStickSerial = response.getSerial();
     }
 
-
     public void enterControlMode() throws IOException, TimeoutException, UnexpectedMessageException, ChecksumException, EncryptionException {
         boolean doRetry;
 
@@ -185,7 +85,7 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler {
                         .send(mDevice).checkControlMessage(ContourNextLinkCommandMessage.ASCII.ACK);
             } catch (UnexpectedMessageException e2) {
                 try {
-                    new ContourNextLinkCommandMessage(ContourNextLinkCommandMessage.ASCII.EOT).send(this);
+                    new ContourNextLinkCommandMessage(ContourNextLinkCommandMessage.ASCII.EOT).send(mDevice);
                 } catch (IOException e) {}
                 finally {
                     doRetry = true;
@@ -205,11 +105,9 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler {
         Log.d(TAG, "Finished enterPasshtroughMode");
     }
 
-    public void openConnection() throws IOException, TimeoutException, NoSuchAlgorithmException, ChecksumException {
+    public void openConnection() throws IOException, TimeoutException, NoSuchAlgorithmException, ChecksumException, EncryptionException {
         Log.d(TAG, "Begin openConnection");
-        new ContourNextLinkBinaryMessage(ContourNextLinkBinaryMessage.CommandType.OPEN_CONNECTION, mPumpSession, mPumpSession.getHMAC()).send(this);
-        // FIXME - We need to care what the response message is - wrong MAC and all that
-        readMessage();
+        new OpenConnectionRequestMessage(mPumpSession, mPumpSession.getHMAC()).send(mDevice);
         Log.d(TAG, "Finished openConnection");
     }
 
@@ -234,7 +132,7 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler {
         Log.d(TAG, String.format("Finished requestLinkKey. linkKey = '%s'", this.getPumpSession().getKey()));
     }
 
-    public byte negotiateChannel(byte lastRadioChannel) throws IOException, ChecksumException, TimeoutException {
+    public byte negotiateChannel(byte lastRadioChannel) throws IOException, ChecksumException, TimeoutException, EncryptionException {
         ArrayList<Byte> radioChannels = new ArrayList<>(Arrays.asList(ArrayUtils.toObject(RADIO_CHANNELS)));
 
         if (lastRadioChannel != 0x00) {
@@ -250,26 +148,12 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler {
         for (byte channel : radioChannels) {
             Log.d(TAG, String.format("negotiateChannel: trying channel '%d'...", channel));
             mPumpSession.setRadioChannel(channel);
-            new ChannelNegotiateMessage(mPumpSession).send(this);
-
-            // Don't care what the 0x81 response message is at this stage
-            Log.d(TAG, "negotiateChannel: Reading 0x81 message");
-            readMessage();
-            // The 0x80 message
-            Log.d(TAG, "negotiateChannel: Reading 0x80 message");
-            ContourNextLinkMessage response = ContourNextLinkBinaryMessage.fromBytes(readMessage());
-            byte[] responseBytes = response.encode();
-
-            Log.d(TAG, "negotiateChannel: Check response length");
-            if (responseBytes.length > 46) {
-                // Looks promising, let's check the last byte of the payload to make sure
-                if (responseBytes[76] == mPumpSession.getRadioChannel()) {
-                    break;
-                } else {
-                    throw new IOException(String.format(Locale.getDefault(), "Expected to get a message for channel %d. Got %d", mPumpSession.getRadioChannel(), responseBytes[76]));
-                }
+            ChannelNegotiateResponseMessage response = new ChannelNegotiateRequestMessage(mPumpSession).send(mDevice);
+
+            if (response.getRadioChannel() == mPumpSession.getRadioChannel()) {
+                break;
             } else {
-                mPumpSession.setRadioChannel((byte) 0);
+                throw new IOException(String.format(Locale.getDefault(), "Expected to get a message for channel %d. Got %d", mPumpSession.getRadioChannel(), response.getRadioChannel()));
             }
         }
 
@@ -279,9 +163,7 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler {
 
     public void beginEHSMSession() throws EncryptionException, IOException, TimeoutException, ChecksumException {
         Log.d(TAG, "Begin beginEHSMSession");
-        new BeginEHSMMessage(mPumpSession).send(this);
-        // The Begin EHSM Session only has an 0x81 response
-        readMessage();
+        new BeginEHSMMessage(mPumpSession).send(mDevice);
         Log.d(TAG, "Finished beginEHSMSession");
     }
 
@@ -289,172 +171,42 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler {
         Log.d(TAG, "Begin getPumpTime");
         // FIXME - throw if not in EHSM mode (add a state machine)
 
-        new PumpTimeRequestMessage(mPumpSession).send(this);
-        // Read the 0x81
-        readMessage();
-
-        // Read the 0x80
-        ContourNextLinkMessage response = PumpTimeResponseMessage.fromBytes(mPumpSession, readMessage());
-
-        if (response.encode().length < (61 + 8)) {
-            // Invalid message. Return an invalid date.
-            // TODO - deal with this more elegantly
-            Log.e(TAG, "Invalid message received for getPumpTime");
-            return new Date();
-        }
-
-        // FIXME - this needs to go into PumpTimeResponseMessage
-        ByteBuffer dateBuffer = ByteBuffer.allocate(8);
-        dateBuffer.order(ByteOrder.BIG_ENDIAN);
-        dateBuffer.put(response.encode(), 0x3d, 8);
-        long rtc = dateBuffer.getInt(0) & 0x00000000ffffffffL;
-        long offset = dateBuffer.getInt(4);
+        PumpTimeResponseMessage response = new PumpTimeRequestMessage(mPumpSession).send(mDevice);
 
-        Log.d(TAG, "Finished getPumpTime with date " + MessageUtils.decodeDateTime(rtc, offset));
-        return MessageUtils.decodeDateTime(rtc, offset);
+        Log.d(TAG, "Finished getPumpTime with date " + response.getPumpTime());
+        return response.getPumpTime();
     }
 
-    public void getPumpStatus(PumpStatusEvent pumpRecord, long pumpTimeOffset) throws IOException, EncryptionException, ChecksumException, TimeoutException {
-        Log.d(TAG, "Begin getPumpStatus");
-        // FIXME - throw if not in EHSM mode (add a state machine)
-
-        new PumpStatusRequestMessage(mPumpSession).send(this);
-        // Read the 0x81
-        readMessage();
-
-        // Read the 0x80
-        ContourNextLinkMessage response = PumpStatusResponseMessage.fromBytes(mPumpSession, readMessage());
+    public PumpStatusEvent updatePumpStatus(PumpStatusEvent pumpRecord) throws IOException, EncryptionException, ChecksumException, TimeoutException {
+        Log.d(TAG, "Begin updatePumpStatus");
 
-        if (response.encode().length < (57 + 96)) {
-            // Invalid message. Don't try and parse it
-            // TODO - deal with this more elegantly
-            Log.e(TAG, "Invalid message received for getPumpStatus");
-            return;
-        }
-
-        // FIXME - this needs to go into PumpStatusResponseMessage
-        ByteBuffer statusBuffer = ByteBuffer.allocate(96);
-        statusBuffer.order(ByteOrder.BIG_ENDIAN);
-        statusBuffer.put(response.encode(), 0x39, 96);
-
-        // Status Flags
-        pumpRecord.setSuspended((statusBuffer.get(0x03) & 0x01) != 0x00);
-        pumpRecord.setBolusing((statusBuffer.get(0x03) & 0x02) != 0x00);
-        pumpRecord.setDeliveringInsulin((statusBuffer.get(0x03) & 0x10) != 0x00);
-        pumpRecord.setTempBasalActive((statusBuffer.get(0x03) & 0x20) != 0x00);
-        pumpRecord.setCgmActive((statusBuffer.get(0x03) & 0x40) != 0x00);
-
-        // Active basal pattern
-        pumpRecord.setActiveBasalPattern(statusBuffer.get(0x1a));
-
-        // Normal basal rate
-        long rawNormalBasal = statusBuffer.getInt(0x1b);
-        pumpRecord.setBasalRate(new BigDecimal(rawNormalBasal / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue());
-
-        // Temp basal rate
-        // TODO - need to figure this one out
-        //long rawTempBasal = statusBuffer.getShort(0x21) & 0x0000ffff;
-        //pumpRecord.setTempBasalRate(new BigDecimal(rawTempBasal / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue());
-
-        // Temp basal percentage
-        pumpRecord.setTempBasalPercentage(statusBuffer.get(0x23));
-
-        // Temp basal minutes remaining
-        pumpRecord.setTempBasalMinutesRemaining((short) (statusBuffer.getShort(0x24) & 0x0000ffff));
-
-        // Units of insulin delivered as basal today
-        // TODO - is this basal? Do we have a total Units delivered elsewhere?
-        pumpRecord.setBasalUnitsDeliveredToday(statusBuffer.getInt(0x26));
-
-        // Pump battery percentage
-        pumpRecord.setBatteryPercentage((statusBuffer.get(0x2a)));
-
-        // Reservoir amount
-        long rawReservoirAmount = statusBuffer.getInt(0x2b);
-        pumpRecord.setReservoirAmount(new BigDecimal(rawReservoirAmount / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue());
-
-        // Amount of insulin left in pump (in minutes)
-        byte insulinHours = statusBuffer.get(0x2f);
-        byte insulinMinutes = statusBuffer.get(0x30);
-        pumpRecord.setMinutesOfInsulinRemaining((short) ((insulinHours * 60) + insulinMinutes));
-
-        // Active insulin
-        long rawActiveInsulin = statusBuffer.getShort(0x33) & 0x0000ffff;
-        pumpRecord.setActiveInsulin(new BigDecimal(rawActiveInsulin / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue());
-
-        // CGM SGV
-        pumpRecord.setSgv(statusBuffer.getShort(0x35) & 0x0000ffff); // In mg/DL. 0 means no CGM reading
-
-        // SGV Date
-        long rtc;
-        long offset;
-        if ((pumpRecord.getSgv() & 0x200) == 0x200) {
-            // Sensor error. Let's reset. FIXME - solve this more elegantly later
-            pumpRecord.setSgv(0);
-            rtc = 0;
-            offset = 0;
-            pumpRecord.setCgmTrend(PumpStatusEvent.CGM_TREND.NOT_SET);
-        } else {
-            rtc = statusBuffer.getInt(0x37) & 0x00000000ffffffffL;
-            offset = statusBuffer.getInt(0x3b);
-            pumpRecord.setCgmTrend(fromMessageByte(statusBuffer.get(0x40)));
-        }
-        // TODO - this should go in the sgvDate, and eventDate should be the time of this poll.
-        pumpRecord.setEventDate(new Date(MessageUtils.decodeDateTime(rtc, offset).getTime() - pumpTimeOffset));
-
-        // Predictive low suspend
-        // TODO - there is more status info in this byte other than just a boolean yes/no
-        pumpRecord.setLowSuspendActive(statusBuffer.get(0x3f) != 0);
+        // FIXME - throw if not in EHSM mode (add a state machine)
+        PumpStatusResponseMessage response = new PumpStatusRequestMessage(mPumpSession).send(mDevice);
+        response.updatePumpRecord(pumpRecord);
 
-        // Recent Bolus Wizard BGL
-        pumpRecord.setRecentBolusWizard(statusBuffer.get(0x48) != 0);
-        pumpRecord.setBolusWizardBGL(statusBuffer.getShort(0x49) & 0x0000ffff); // In mg/DL
+        Log.d(TAG, "Finished updatePumpStatus");
 
-        Log.d(TAG, "Finished getPumpStatus");
+        return pumpRecord;
     }
 
     public void getBasalPatterns() throws EncryptionException, IOException, ChecksumException, TimeoutException {
         Log.d(TAG, "Begin getBasalPatterns");
         // FIXME - throw if not in EHSM mode (add a state machine)
 
-        new PumpBasalPatternRequestMessage(mPumpSession).send(this);
-        // Read the 0x81
-        readMessage();
-
-        // Read the 0x80
-        ContourNextLinkMessage response = PumpBasalPatternResponseMessage.fromBytes(mPumpSession, readMessage());
-
-        // TODO - determine message validity
-        /*
-        if (response.encode().length < (61 + 8)) {
-            // Invalid message.
-            // TODO - deal with this more elegantly
-            Log.e(TAG, "Invalid message received for getBasalPatterns");
-            return;
-        }
-        */
-
-        // FIXME - this needs to go into PumpBasalPatternResponseMessage
-        ByteBuffer basalRatesBuffer = ByteBuffer.allocate(96);
-        basalRatesBuffer.order(ByteOrder.BIG_ENDIAN);
-        basalRatesBuffer.put(response.encode(), 0x39, 96);
+        PumpBasalPatternResponseMessage response = new PumpBasalPatternRequestMessage(mPumpSession).send(mDevice);
 
         Log.d(TAG, "Finished getBasalPatterns");
     }
 
     public void endEHSMSession() throws EncryptionException, IOException, TimeoutException, ChecksumException {
         Log.d(TAG, "Begin endEHSMSession");
-        new EndEHSMMessage(mPumpSession).send(this);
-        // The End EHSM Session only has an 0x81 response
-        readMessage();
+        new EndEHSMMessage(mPumpSession).send(mDevice);
         Log.d(TAG, "Finished endEHSMSession");
     }
 
-    public void closeConnection() throws IOException, TimeoutException, ChecksumException {
+    public void closeConnection() throws IOException, TimeoutException, ChecksumException, EncryptionException, NoSuchAlgorithmException {
         Log.d(TAG, "Begin closeConnection");
-        new ContourNextLinkBinaryMessage(ContourNextLinkBinaryMessage.CommandType.CLOSE_CONNECTION, mPumpSession, null).send(this);
-        // FIXME - We need to care what the response message is - wrong MAC and all that
-        readMessage();
+        new CloseConnectionRequestMessage(mPumpSession, mPumpSession.getHMAC()).send(mDevice);
         Log.d(TAG, "Finished closeConnection");
     }
 
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/BeginEHSMMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/BeginEHSMMessage.java
index 804ce6d..d43c2c8 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/BeginEHSMMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/BeginEHSMMessage.java
@@ -1,6 +1,7 @@
 package info.nightscout.android.medtronic.message;
 
 import java.io.IOException;
+import java.util.concurrent.TimeoutException;
 
 import info.nightscout.android.USB.UsbHidDriver;
 import info.nightscout.android.medtronic.MedtronicCnlSession;
@@ -18,8 +19,10 @@ public class BeginEHSMMessage extends MedtronicRequestMessage {
         return new byte[] { 0x00 };
     }
 
-    protected void sendMessage(UsbHidDriver mDevice) throws IOException {
-        super.sendMessage(mDevice);
-        mPumpSession.incrMedtronicSequenceNumber();
+    public void send(UsbHidDriver mDevice) throws IOException, TimeoutException {
+        sendMessage(mDevice);
+
+        // The Begin EHSM Session only has an 0x81 response
+        readMessage(mDevice);
     }
 }
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateRequestMessage.java
similarity index 50%
rename from app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateMessage.java
rename to app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateRequestMessage.java
index 544caaf..5121641 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateRequestMessage.java
@@ -1,18 +1,37 @@
 package info.nightscout.android.medtronic.message;
 
+import android.util.Log;
+
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.util.concurrent.TimeoutException;
 
+import info.nightscout.android.USB.UsbHidDriver;
 import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 /**
  * Created by lgoedhart on 26/03/2016.
  */
-public class ChannelNegotiateMessage extends MedtronicRequestMessage {
-    public ChannelNegotiateMessage(MedtronicCnlSession pumpSession) throws ChecksumException {
+public class ChannelNegotiateRequestMessage extends MedtronicRequestMessage {
+    private static final String TAG = ChannelNegotiateRequestMessage.class.getSimpleName();
+
+    public ChannelNegotiateRequestMessage(MedtronicCnlSession pumpSession) throws ChecksumException {
         super(CommandType.SEND_MESSAGE, CommandAction.CHANNEL_NEGOTIATE, pumpSession, buildPayload(pumpSession));
     }
 
+    public ChannelNegotiateResponseMessage send(UsbHidDriver mDevice) throws IOException, TimeoutException, ChecksumException, EncryptionException {
+
+        // Don't care what the 0x81 response message is at this stage
+        Log.d(TAG, "negotiateChannel: Reading 0x81 message");
+        readMessage(mDevice);
+        // The 0x80 message
+        Log.d(TAG, "negotiateChannel: Reading 0x80 message");
+        ChannelNegotiateResponseMessage response = new ChannelNegotiateResponseMessage(mPumpSession, readMessage(mDevice));
+
+        return response;
+    }
+
     protected static byte[] buildPayload( MedtronicCnlSession pumpSession ) {
         ByteBuffer payload = ByteBuffer.allocate(26);
         payload.order(ByteOrder.LITTLE_ENDIAN);
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateResponseMessage.java
new file mode 100644
index 0000000..39a75d6
--- /dev/null
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateResponseMessage.java
@@ -0,0 +1,31 @@
+package info.nightscout.android.medtronic.message;
+
+import android.util.Log;
+
+import info.nightscout.android.medtronic.MedtronicCnlSession;
+
+/**
+ * Created by lgoedhart on 27/03/2016.
+ */
+public class ChannelNegotiateResponseMessage extends MedtronicResponseMessage {
+    private static final String TAG = ChannelNegotiateResponseMessage.class.getSimpleName();
+
+    private byte radioChannel = 0;
+
+    protected ChannelNegotiateResponseMessage(MedtronicCnlSession pumpSession, byte[] payload) throws EncryptionException, ChecksumException {
+        super(pumpSession, payload);
+
+        byte[] responseBytes = this.encode();
+
+        Log.d(TAG, "negotiateChannel: Check response length");
+        if (responseBytes.length > 46) {
+            radioChannel = responseBytes[76];
+        } else {
+            radioChannel = ((byte) 0);
+        }
+    }
+
+    public byte getRadioChannel() {
+        return radioChannel;
+    }
+}
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryRequestMessage.java
index 24238af..b560cfe 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryRequestMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryRequestMessage.java
@@ -4,7 +4,6 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.Locale;
-import java.util.concurrent.TimeoutException;
 
 import info.nightscout.android.USB.UsbHidDriver;
 import info.nightscout.android.medtronic.MedtronicCnlSession;
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryResponseMessage.java
index 549ee88..89adf99 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryResponseMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryResponseMessage.java
@@ -1,9 +1,5 @@
 package info.nightscout.android.medtronic.message;
 
-import java.io.IOException;
-import java.util.Locale;
-import java.util.concurrent.TimeoutException;
-
 /**
  * Created by lgoedhart on 26/03/2016.
  */
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkCommandMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkCommandMessage.java
index 9839f75..b5edb78 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkCommandMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkCommandMessage.java
@@ -4,7 +4,6 @@ import java.io.IOException;
 import java.util.concurrent.TimeoutException;
 
 import info.nightscout.android.USB.UsbHidDriver;
-import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 /**
  * Created by lgoedhart on 26/03/2016.
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkCommandResponse.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkCommandResponse.java
index 12f9b95..4cbf415 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkCommandResponse.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkCommandResponse.java
@@ -1,7 +1,5 @@
 package info.nightscout.android.medtronic.message;
 
-import info.nightscout.android.medtronic.MedtronicCnlSession;
-
 /**
  * Created by volker on 10.12.2016.
  */
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java
index 36d39a4..1c8d92a 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java
@@ -5,15 +5,11 @@ import android.util.Log;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.util.Locale;
 import java.util.concurrent.TimeoutException;
 
 import info.nightscout.android.USB.UsbHidDriver;
-import info.nightscout.android.medtronic.MedtronicCnlSession;
 import info.nightscout.android.utils.HexDump;
 
-import static android.R.id.message;
-
 /**
  * Created by lgoedhart on 26/03/2016.
  */
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessageHandler.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessageHandler.java
deleted file mode 100644
index 1d669f6..0000000
--- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessageHandler.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package info.nightscout.android.medtronic.message;
-
-import java.io.IOException;
-
-/**
- * Created by lgoedhart on 26/03/2016.
- */
-public interface ContourNextLinkMessageHandler {
-    void sendMessage( ContourNextLinkMessage message ) throws IOException;
-    ContourNextLinkMessage receiveMessage();
-}
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkRequestMessage.java
index 390380d..036c185 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkRequestMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkRequestMessage.java
@@ -1,14 +1,6 @@
 package info.nightscout.android.medtronic.message;
 
 
-import java.io.IOException;
-import java.util.Locale;
-import java.util.concurrent.TimeoutException;
-
-import info.nightscout.android.medtronic.MedtronicCnlSession;
-import info.nightscout.android.medtronic.message.ContourNextLinkMessage;
-import info.nightscout.android.medtronic.message.UnexpectedMessageException;
-
 /**
  * Created by volker on 12.12.2016.
  */
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkResponseMessage.java
index 24d6aa8..3dd5141 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkResponseMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkResponseMessage.java
@@ -1,17 +1,9 @@
 package info.nightscout.android.medtronic.message;
 
 import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
 import java.util.Locale;
 import java.util.concurrent.TimeoutException;
 
-import info.nightscout.android.USB.UsbHidDriver;
-import info.nightscout.android.medtronic.MedtronicCnlSession;
-import info.nightscout.android.medtronic.message.ChecksumException;
-import info.nightscout.android.medtronic.message.ContourNextLinkMessage;
-import info.nightscout.android.medtronic.message.UnexpectedMessageException;
-
 /**
  * Created by lgoedhart on 26/03/2016.
  */
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/DeviceInfoResponseCommandMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/DeviceInfoResponseCommandMessage.java
index 1400a26..fec7c00 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/DeviceInfoResponseCommandMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/DeviceInfoResponseCommandMessage.java
@@ -6,10 +6,6 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import info.nightscout.android.medtronic.MedtronicCnlSession;
-import info.nightscout.android.medtronic.message.ChecksumException;
-import info.nightscout.android.medtronic.message.EncryptionException;
-import info.nightscout.android.medtronic.message.MedtronicResponseMessage;
-import info.nightscout.android.medtronic.message.UnexpectedMessageException;
 
 /**
  * Created by lgoedhart on 10/05/2016.
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/EndEHSMMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/EndEHSMMessage.java
index fc7c4dd..9db6c03 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/EndEHSMMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/EndEHSMMessage.java
@@ -1,6 +1,7 @@
 package info.nightscout.android.medtronic.message;
 
 import java.io.IOException;
+import java.util.concurrent.TimeoutException;
 
 import info.nightscout.android.USB.UsbHidDriver;
 import info.nightscout.android.medtronic.MedtronicCnlSession;
@@ -18,8 +19,10 @@ public class EndEHSMMessage extends MedtronicRequestMessage {
         return new byte[] { 0x01 };
     }
 
-    protected void sendMessage(UsbHidDriver mDevice) throws IOException {
-        super.sendMessage(mDevice);
-        mPumpSession.incrMedtronicSequenceNumber();
+    public void send(UsbHidDriver mDevice) throws IOException, TimeoutException {
+        sendMessage(mDevice);
+
+        // The End EHSM Session only has an 0x81 response
+        readMessage(mDevice);
     }
 }
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicResponseMessage.java
index 8557561..dcc926c 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicResponseMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicResponseMessage.java
@@ -1,13 +1,13 @@
 package info.nightscout.android.medtronic.message;
 
-import info.nightscout.android.medtronic.MedtronicCnlSession;
-
 import java.nio.ByteBuffer;
 
 import javax.crypto.Cipher;
 import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
+import info.nightscout.android.medtronic.MedtronicCnlSession;
+
 /**
  * Created by lgoedhart on 26/03/2016.
  */
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/OpenConnectionResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/OpenConnectionResponseMessage.java
index 38f4ffb..b061ebd 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/OpenConnectionResponseMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/OpenConnectionResponseMessage.java
@@ -1,8 +1,5 @@
 package info.nightscout.android.medtronic.message;
 
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
 import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 /**
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternRequestMessage.java
index a007153..d341f77 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternRequestMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternRequestMessage.java
@@ -1,6 +1,7 @@
 package info.nightscout.android.medtronic.message;
 
 import java.io.IOException;
+import java.util.concurrent.TimeoutException;
 
 import info.nightscout.android.USB.UsbHidDriver;
 import info.nightscout.android.medtronic.MedtronicCnlSession;
@@ -13,8 +14,13 @@ public class PumpBasalPatternRequestMessage extends MedtronicRequestMessage {
         super(SendMessageType.READ_BASAL_PATTERN_REQUEST, pumpSession, null);
     }
 
-    protected void sendMessage(UsbHidDriver mDevice) throws IOException {
-        super.sendMessage(mDevice);
-        mPumpSession.incrMedtronicSequenceNumber();
+    public PumpBasalPatternResponseMessage send(UsbHidDriver mDevice) throws IOException, TimeoutException, ChecksumException, EncryptionException {
+        // Read the 0x81
+        readMessage(mDevice);
+
+        // Read the 0x80
+        PumpBasalPatternResponseMessage response = new PumpBasalPatternResponseMessage(mPumpSession, readMessage(mDevice));
+
+        return response;
     }
 }
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternResponseMessage.java
index 43132b6..daef88a 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternResponseMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternResponseMessage.java
@@ -1,5 +1,8 @@
 package info.nightscout.android.medtronic.message;
 
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
 import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 /**
@@ -8,14 +11,22 @@ import info.nightscout.android.medtronic.MedtronicCnlSession;
 public class PumpBasalPatternResponseMessage extends MedtronicResponseMessage {
     protected PumpBasalPatternResponseMessage(MedtronicCnlSession pumpSession, byte[] payload) throws EncryptionException, ChecksumException {
         super(pumpSession, payload);
-    }
 
-    public static ContourNextLinkMessage fromBytes(MedtronicCnlSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException {
-        // TODO - turn this into a factory
-        ContourNextLinkMessage message = MedtronicResponseMessage.fromBytes(pumpSession, bytes);
+        // TODO - determine message validity
+        /*
+        if (response.encode().length < (61 + 8)) {
+            // Invalid message.
+            // TODO - deal with this more elegantly
+            Log.e(TAG, "Invalid message received for getBasalPatterns");
+            return;
+        }
+        */
 
-        // TODO - Validate the MessageType
+        // FIXME - this needs to go into PumpBasalPatternResponseMessage
+        ByteBuffer basalRatesBuffer = ByteBuffer.allocate(96);
+        basalRatesBuffer.order(ByteOrder.BIG_ENDIAN);
+        basalRatesBuffer.put(this.encode(), 0x39, 96);
 
-        return message;
     }
+
 }
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusRequestMessage.java
index 0c507a6..9532cb6 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusRequestMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusRequestMessage.java
@@ -1,6 +1,7 @@
 package info.nightscout.android.medtronic.message;
 
 import java.io.IOException;
+import java.util.concurrent.TimeoutException;
 
 import info.nightscout.android.USB.UsbHidDriver;
 import info.nightscout.android.medtronic.MedtronicCnlSession;
@@ -13,8 +14,14 @@ public class PumpStatusRequestMessage extends MedtronicRequestMessage {
         super(SendMessageType.READ_PUMP_STATUS_REQUEST, pumpSession, null);
     }
 
-    protected void sendMessage(UsbHidDriver mDevice) throws IOException {
-        super.sendMessage(mDevice);
-        mPumpSession.incrMedtronicSequenceNumber();
+    public PumpStatusResponseMessage send(UsbHidDriver mDevice) throws IOException, TimeoutException, ChecksumException, EncryptionException {
+        sendMessage(mDevice);
+
+        // Read the 0x81
+        readMessage(mDevice);
+
+        PumpStatusResponseMessage response = new PumpStatusResponseMessage(mPumpSession, readMessage(mDevice));
+
+        return response;
     }
 }
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 151348e..b791a29 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java
@@ -1,21 +1,191 @@
 package info.nightscout.android.medtronic.message;
 
+import android.util.Log;
+
+import java.math.BigDecimal;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Date;
+
 import info.nightscout.android.medtronic.MedtronicCnlSession;
+import info.nightscout.android.model.medtronicNg.PumpStatusEvent;
 
 /**
  * Created by lgoedhart on 27/03/2016.
  */
 public class PumpStatusResponseMessage extends MedtronicResponseMessage {
+    private static final String TAG = PumpStatusResponseMessage.class.getSimpleName();
+
+    // Data from the Medtronic Pump Status message
+    private boolean suspended;
+    private boolean bolusing;
+    private boolean deliveringInsulin;
+    private boolean tempBasalActive;
+    private boolean cgmActive;
+    private byte activeBasalPattern;
+    private float basalRate;
+    private float tempBasalRate;
+    private byte tempBasalPercentage;
+    private short tempBasalMinutesRemaining;
+    private float basalUnitsDeliveredToday;
+    private short batteryPercentage;
+    private float reservoirAmount;
+    private short minutesOfInsulinRemaining; // 25h == "more than 1 day"
+    private float activeInsulin;
+    private int sgv;
+    private Date sgvDate;
+    private boolean lowSuspendActive;
+    private 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 long rtc;
+    private long offset;
+
     protected PumpStatusResponseMessage(MedtronicCnlSession pumpSession, byte[] payload) throws EncryptionException, ChecksumException {
         super(pumpSession, payload);
+
+        if (this.encode().length < (57 + 96)) {
+            // Invalid message. Don't try and parse it
+            // TODO - deal with this more elegantly
+            Log.e(TAG, "Invalid message received for updatePumpStatus");
+            return;
+        }
+
+        // FIXME - this needs to go into PumpStatusResponseMessage
+        ByteBuffer statusBuffer = ByteBuffer.allocate(96);
+        statusBuffer.order(ByteOrder.BIG_ENDIAN);
+        statusBuffer.put(this.encode(), 0x39, 96);
+
+        // 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);
+
+        // Active basal pattern
+        activeBasalPattern = (statusBuffer.get(0x1a));
+
+        // Normal basal rate
+        long rawNormalBasal = statusBuffer.getInt(0x1b);
+        basalRate = (new BigDecimal(rawNormalBasal / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue());
+
+        // Temp basal rate
+        // TODO - need to figure this one out
+        //long rawTempBasal = statusBuffer.getShort(0x21) & 0x0000ffff;
+        //pumpRecord.setTempBasalRate(new BigDecimal(rawTempBasal / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue());
+
+        // Temp basal percentage
+        tempBasalPercentage = (statusBuffer.get(0x23));
+
+        // Temp basal minutes remaining
+        tempBasalMinutesRemaining = ((short) (statusBuffer.getShort(0x24) & 0x0000ffff));
+
+        // Units of insulin delivered as basal today
+        // TODO - is this basal? Do we have a total Units delivered elsewhere?
+        basalUnitsDeliveredToday = (statusBuffer.getInt(0x26));
+
+        // Pump battery percentage
+        batteryPercentage = ((statusBuffer.get(0x2a)));
+
+        // Reservoir amount
+        long rawReservoirAmount = statusBuffer.getInt(0x2b);
+        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 insulinMinutes = statusBuffer.get(0x30);
+        minutesOfInsulinRemaining = ((short) ((insulinHours * 60) + insulinMinutes));
+
+        // Active insulin
+        long rawActiveInsulin = statusBuffer.getShort(0x33) & 0x0000ffff;
+        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
+
+        // SGV Date
+
+        if ((sgv & 0x200) == 0x200) {
+            // Sensor error. Let's reset. FIXME - solve this more elegantly later
+            sgv = 0;
+            rtc = 0;
+            offset = 0;
+            cgmTrend = (PumpStatusEvent.CGM_TREND.NOT_SET);
+        } else {
+            rtc = statusBuffer.getInt(0x37) & 0x00000000ffffffffL;
+            offset = statusBuffer.getInt(0x3b);
+            cgmTrend = (PumpStatusEvent.CGM_TREND.fromMessageByte(statusBuffer.get(0x40)));
+        }
+
+        // TODO - this should go in the sgvDate, and eventDate should be the time of this poll.
+        sgvDate = MessageUtils.decodeDateTime(rtc, offset);
+
+        // Predictive low suspend
+        // TODO - there is more status info in this byte other than just a boolean yes/no
+        lowSuspendActive = (statusBuffer.get(0x3f) != 0);
+
+        // Recent Bolus Wizard BGL
+        recentBolusWizard = (statusBuffer.get(0x48) != 0);
+        bolusWizardBGL = (statusBuffer.getShort(0x49) & 0x0000ffff); // In mg/DL
     }
 
-    public static ContourNextLinkMessage fromBytes(MedtronicCnlSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException {
-        // TODO - turn this into a factory
-        ContourNextLinkMessage message = MedtronicResponseMessage.fromBytes(pumpSession, bytes);
+    /**
+     * update pumpRecord with data read from pump
+     *
+     * @param pumpRecord
+     */
+    public void updatePumpRecord(PumpStatusEvent pumpRecord) {
+        // Status Flags
+        pumpRecord.setSuspended(suspended);
+        pumpRecord.setBolusing(bolusing);
+        pumpRecord.setDeliveringInsulin(deliveringInsulin);
+        pumpRecord.setTempBasalActive(tempBasalActive);
+        pumpRecord.setCgmActive(cgmActive);
+
+        // Active basal pattern
+        pumpRecord.setActiveBasalPattern(activeBasalPattern);
+
+        // Normal basal rate
+        pumpRecord.setBasalRate(basalRate);
+
+        // Temp basal percentage
+        pumpRecord.setTempBasalPercentage(tempBasalPercentage);
+
+        // Temp basal minutes remaining
+        pumpRecord.setTempBasalMinutesRemaining(tempBasalMinutesRemaining);
+
+        // Units of insulin delivered as basal today
+        pumpRecord.setBasalUnitsDeliveredToday(basalUnitsDeliveredToday);
+
+        // Pump battery percentage
+        pumpRecord.setBatteryPercentage(batteryPercentage);
+
+        // Reservoir amount
+        pumpRecord.setReservoirAmount(reservoirAmount);
+
+        // Amount of insulin left in pump (in minutes)
+        pumpRecord.setMinutesOfInsulinRemaining(minutesOfInsulinRemaining);
+
+        // Active insulin
+        pumpRecord.setActiveInsulin(activeInsulin);
+
+        // CGM SGV
+        pumpRecord.setSgv(sgv);
+
+        // SGV Date
+        pumpRecord.setCgmTrend(cgmTrend);
+        pumpRecord.setEventDate(new Date(sgvDate.getTime() - pumpRecord.getPumpTimeOffset()));
 
-        // TODO - Validate the MessageType
+        // Predictive low suspend
+        // TODO - there is more status info in this byte other than just a boolean yes/no
+        pumpRecord.setLowSuspendActive(lowSuspendActive);
 
-        return message;
+        // Recent Bolus Wizard BGL
+        pumpRecord.setRecentBolusWizard(recentBolusWizard);
+        pumpRecord.setBolusWizardBGL(bolusWizardBGL); // In mg/DL
     }
 }
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeRequestMessage.java
index c773fa1..00343c2 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeRequestMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeRequestMessage.java
@@ -1,6 +1,7 @@
 package info.nightscout.android.medtronic.message;
 
 import java.io.IOException;
+import java.util.concurrent.TimeoutException;
 
 import info.nightscout.android.USB.UsbHidDriver;
 import info.nightscout.android.medtronic.MedtronicCnlSession;
@@ -13,8 +14,13 @@ public class PumpTimeRequestMessage extends MedtronicRequestMessage {
         super(SendMessageType.TIME_REQUEST, pumpSession, null);
     }
 
-    protected void sendMessage(UsbHidDriver mDevice) throws IOException {
-        super.sendMessage(mDevice);
-        mPumpSession.incrMedtronicSequenceNumber();
+    public PumpTimeResponseMessage send(UsbHidDriver mDevice) throws IOException, TimeoutException, ChecksumException, EncryptionException {
+        // Read the 0x81
+        readMessage(mDevice);
+
+        // Read the 0x80
+        PumpTimeResponseMessage response = new PumpTimeResponseMessage(mPumpSession, readMessage(mDevice));
+
+        return response;
     }
 }
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeResponseMessage.java
index 6f1ca90..3867391 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeResponseMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeResponseMessage.java
@@ -1,21 +1,41 @@
 package info.nightscout.android.medtronic.message;
 
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Date;
+
 import info.nightscout.android.medtronic.MedtronicCnlSession;
 
 /**
  * Created by lgoedhart on 27/03/2016.
  */
 public class PumpTimeResponseMessage extends MedtronicResponseMessage {
+    private static final String TAG = PumpTimeResponseMessage.class.getSimpleName();
+
+    private Date pumpTime;
+
     protected PumpTimeResponseMessage(MedtronicCnlSession pumpSession, byte[] payload) throws EncryptionException, ChecksumException {
         super(pumpSession, payload);
-    }
 
-    public static ContourNextLinkMessage fromBytes(MedtronicCnlSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException {
-        // TODO - turn this into a factory
-        ContourNextLinkMessage message = MedtronicResponseMessage.fromBytes(pumpSession, bytes);
+        if (this.encode().length < (61 + 8)) {
+            // Invalid message. Return an invalid date.
+            // TODO - deal with this more elegantly
+            Log.e(TAG, "Invalid message received for getPumpTime");
+            pumpTime = new Date();
+        }
 
-        // TODO - Validate the MessageType
+        // FIXME - this needs to go into PumpTimeResponseMessage
+        ByteBuffer dateBuffer = ByteBuffer.allocate(8);
+        dateBuffer.order(ByteOrder.BIG_ENDIAN);
+        dateBuffer.put(this.encode(), 0x3d, 8);
+        long rtc = dateBuffer.getInt(0) & 0x00000000ffffffffL;
+        long offset = dateBuffer.getInt(4);
+        pumpTime = MessageUtils.decodeDateTime(rtc, offset);
+    }
 
-        return message;
+    public Date getPumpTime() {
+        return pumpTime;
     }
 }
diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ReadInfoRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ReadInfoRequestMessage.java
index dc2a97b..a15ffa5 100644
--- a/app/src/main/java/info/nightscout/android/medtronic/message/ReadInfoRequestMessage.java
+++ b/app/src/main/java/info/nightscout/android/medtronic/message/ReadInfoRequestMessage.java
@@ -1,12 +1,11 @@
 package info.nightscout.android.medtronic.message;
 
 import java.io.IOException;
+import java.util.concurrent.TimeoutException;
 
 import info.nightscout.android.USB.UsbHidDriver;
 import info.nightscout.android.medtronic.MedtronicCnlSession;
 
-import java.util.concurrent.TimeoutException;
-
 /**
  * Created by volker on 10.12.2016.
  */
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 814f29d..2451ecb 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
@@ -235,7 +235,7 @@ public class MedtronicCnlIntentService extends IntentService {
                     // TODO - send ACTION to MainActivity to show offset between pump and uploader.
                     pumpRecord.setPumpTimeOffset(pumpOffset);
                     pumpRecord.setPumpDate(new Date(pumpTime - pumpOffset));
-                    cnlReader.getPumpStatus(pumpRecord, pumpOffset);
+                    cnlReader.updatePumpStatus(pumpRecord);
                     activePump.getPumpHistory().add(pumpRecord);
 
                     cnlReader.endEHSMSession();
@@ -270,10 +270,12 @@ public class MedtronicCnlIntentService extends IntentService {
                 Log.e(TAG, "Could not determine CNL HMAC", e);
                 sendStatus("Error connecting to Contour Next Link: Hashing error.");
             } finally {
-                //TODO : 05.11.2016 has the close to be here?
-                cnlReader.closeConnection();
-                cnlReader.endPassthroughMode();
-                cnlReader.endControlMode();
+                try {
+                    cnlReader.closeConnection();
+                    cnlReader.endPassthroughMode();
+                    cnlReader.endControlMode();
+                } catch (NoSuchAlgorithmException e) {}
+
             }
         } catch (IOException e) {
             Log.e(TAG, "Error connecting to Contour Next Link.", e);
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 ab95d50..d73f3e6 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
@@ -272,6 +272,27 @@ public class PumpStatusEvent extends RealmObject {
         DOUBLE_DOWN,
         NOT_COMPUTABLE,
         RATE_OUT_OF_RANGE,
-        NOT_SET
+        NOT_SET;
+
+        public static CGM_TREND fromMessageByte(byte messageByte) {
+            switch (messageByte) {
+                case (byte) 0x60:
+                    return PumpStatusEvent.CGM_TREND.FLAT;
+                case (byte) 0xc0:
+                    return PumpStatusEvent.CGM_TREND.DOUBLE_UP;
+                case (byte) 0xa0:
+                    return PumpStatusEvent.CGM_TREND.SINGLE_UP;
+                case (byte) 0x80:
+                    return PumpStatusEvent.CGM_TREND.FOURTY_FIVE_UP;
+                case (byte) 0x40:
+                    return PumpStatusEvent.CGM_TREND.FOURTY_FIVE_DOWN;
+                case (byte) 0x20:
+                    return PumpStatusEvent.CGM_TREND.SINGLE_DOWN;
+                case (byte) 0x00:
+                    return PumpStatusEvent.CGM_TREND.DOUBLE_DOWN;
+                default:
+                    return PumpStatusEvent.CGM_TREND.NOT_COMPUTABLE;
+            }
+        }
     }
 }
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 b542315..bcacc34 100644
--- a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadIntentService.java
+++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadIntentService.java
@@ -10,8 +10,6 @@ import android.preference.PreferenceManager;
 import android.support.v4.content.LocalBroadcastManager;
 import android.util.Log;
 
-import org.apache.http.HttpResponse;
-import org.apache.http.StatusLine;
 import org.apache.http.client.ResponseHandler;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.entity.StringEntity;
-- 
GitLab