From af35253f0bf1256b34c566ca65bfdce25ed61573 Mon Sep 17 00:00:00 2001 From: Bart Sopers Date: Wed, 10 Feb 2021 15:16:28 +0100 Subject: [PATCH] Add SetUniqueIdCommand and DeactivateCommand; add captures for ProgramAlertsCommand --- .../dash/driver/pod/command/CommandBase.java | 20 +++- .../driver/pod/command/DeactivateCommand.java | 21 ++++ .../driver/pod/command/GetVersionCommand.java | 13 +-- .../pod/command/SetUniqueIdCommand.java | 50 +++++++++ .../pod/command/DeactivateCommandTest.java | 17 +++ .../pod/command/ProgramAlertsCommandTest.java | 102 ++++++++++++++++++ .../pod/command/SetUniqueIdCommandTest.java | 19 ++++ 7 files changed, 230 insertions(+), 12 deletions(-) create mode 100644 omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/DeactivateCommand.java create mode 100644 omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/SetUniqueIdCommand.java create mode 100644 omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/DeactivateCommandTest.java create mode 100644 omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/ProgramAlertsCommandTest.java create mode 100644 omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/SetUniqueIdCommandTest.java diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/CommandBase.java b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/CommandBase.java index 9e4c38c24e..b9c92f8b4c 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/CommandBase.java +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/CommandBase.java @@ -8,22 +8,36 @@ import java.util.List; import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.CrcUtil; abstract class CommandBase implements Command { - final CommandType commandType; - final short sequenceNumber; + static final short HEADER_LENGTH = 6; - CommandBase(CommandType commandType, short sequenceNumber) { + final CommandType commandType; + final int address; + final short sequenceNumber; + final boolean unknown; + + CommandBase(CommandType commandType, int address, short sequenceNumber, boolean unknown) { this.commandType = commandType; + this.address = address; this.sequenceNumber = sequenceNumber; + this.unknown = unknown; } @Override public CommandType getCommandType() { return commandType; } + public int getAddress() { + return address; + } + public short getSequenceNumber() { return sequenceNumber; } + public boolean isUnknown() { + return unknown; + } + static byte[] formatCommand(byte[] command) { List temp = new ArrayList<>(); diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/DeactivateCommand.java b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/DeactivateCommand.java new file mode 100644 index 0000000000..d7678f8cb7 --- /dev/null +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/DeactivateCommand.java @@ -0,0 +1,21 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command; + +import java.nio.ByteBuffer; + +public class DeactivateCommand extends CommandBase { + private static final short LENGTH = 6; + private static final byte BODY_LENGTH = 4; + + DeactivateCommand(int address, short sequenceNumber, boolean unknown) { + super(CommandType.DEACTIVATE, address, sequenceNumber, unknown); + } + + @Override public byte[] getEncoded() { + return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) // + .put(encodeHeader(address, sequenceNumber, LENGTH, unknown)) // + .put(commandType.getValue()) // + .put(BODY_LENGTH) // + .putInt(1229869870) // FIXME ?? was: byte array of int 777211465 converted to little endian + .array()); + } +} diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/GetVersionCommand.java b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/GetVersionCommand.java index 782e4c9b58..9914c59889 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/GetVersionCommand.java +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/GetVersionCommand.java @@ -7,21 +7,16 @@ public class GetVersionCommand extends CommandBase { private static final short LENGTH = 6; private static final byte BODY_LENGTH = 4; - private final int address; - private final boolean unknown; - public GetVersionCommand(short sequenceNumber, boolean unknown) { - this(sequenceNumber, DEFAULT_ADDRESS, unknown); + this(DEFAULT_ADDRESS, sequenceNumber, unknown); } - public GetVersionCommand(short sequenceNumber, int address, boolean unknown) { - super(CommandType.GET_VERSION, sequenceNumber); - this.address = address; - this.unknown = unknown; + public GetVersionCommand(int address, short sequenceNumber, boolean unknown) { + super(CommandType.GET_VERSION, address, sequenceNumber, unknown); } @Override public byte[] getEncoded() { - return appendCrc(ByteBuffer.allocate(12) // + return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) // .put(encodeHeader(address, sequenceNumber, LENGTH, unknown)) // .put(commandType.getValue()) // .put(BODY_LENGTH) // diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/SetUniqueIdCommand.java b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/SetUniqueIdCommand.java new file mode 100644 index 0000000000..3e4e8186c2 --- /dev/null +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/SetUniqueIdCommand.java @@ -0,0 +1,50 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command; + +import java.nio.ByteBuffer; +import java.util.Calendar; +import java.util.Date; + +public class SetUniqueIdCommand extends CommandBase { + private static final int DEFAULT_ADDRESS = -1; + private static final short LENGTH = 21; + private static final byte BODY_LENGTH = 19; + + private final int lotNumber; + private final int podSequenceNumber; + private final Date initializationTime; + + SetUniqueIdCommand(int address, short sequenceNumber, int lotNumber, int podSequenceNumber, Date initializationTime, boolean unknown) { + super(CommandType.SET_UNIQUE_ID, address, sequenceNumber, unknown); + this.lotNumber = lotNumber; + this.podSequenceNumber = podSequenceNumber; + this.initializationTime = initializationTime; + } + + @Override public byte[] getEncoded() { + return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) // + .put(encodeHeader(DEFAULT_ADDRESS, sequenceNumber, LENGTH, unknown)) // + .put(commandType.getValue()) // + .put(BODY_LENGTH) // + .putInt(address) // + .put((byte) 0x14) // FIXME ?? + .put((byte) 0x04) // FIXME ?? + .put(encodeInitializationTime(initializationTime)) // + .putInt(lotNumber) // + .putInt(podSequenceNumber) // + .array()); + + } + + private static byte[] encodeInitializationTime(Date date) { + Calendar instance = Calendar.getInstance(); + instance.setTime(date); + + return new byte[]{ // + (byte) (instance.get(Calendar.MONTH) + 1), // + (byte) instance.get(Calendar.DATE), // + (byte) (instance.get(Calendar.YEAR) % 100), // + (byte) instance.get(Calendar.HOUR_OF_DAY), // + (byte) instance.get(Calendar.MINUTE) // + }; + } +} diff --git a/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/DeactivateCommandTest.java b/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/DeactivateCommandTest.java new file mode 100644 index 0000000000..b0b4b5380b --- /dev/null +++ b/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/DeactivateCommandTest.java @@ -0,0 +1,17 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command; + +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; + +public class DeactivateCommandTest { + @Test + public void testEncoding() throws DecoderException { + byte[] encoded = new DeactivateCommand(37879809, (short) 5, false) // + .getEncoded(); + + assertArrayEquals(Hex.decodeHex("0242000114061C04494E532E001C"), encoded); + } +} \ No newline at end of file diff --git a/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/ProgramAlertsCommandTest.java b/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/ProgramAlertsCommandTest.java new file mode 100644 index 0000000000..ad642d6c4e --- /dev/null +++ b/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/ProgramAlertsCommandTest.java @@ -0,0 +1,102 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command; + +public class ProgramAlertsCommandTest { + // TODO + + // Address for all captures below: 37879811 + + /* + Pod expiration alerts + + V/PodComm: *** encode() CALLED ON ProgramAlertsCommandEncoder + *** encode() CALLED ON HeaderEncoder + V/PodComm: *** encode() RESULT FOR HeaderEncoder: 02:42:00:03:8C:12 FROM HeaderEncoder{lengthSequenceNumberAndFlags=-29678, f6666a=[], encoded=[2, 66, 0, 3, -116, 18], headerEncoder=null, commandId=0, f6671f=0, commandBodyLength=0} + *** encode() RESULT FOR ProgramAlertsCommandEncoder: 02:42:00:03:8C:12:19:10:49:4E:53:2E:79:A4:10:D1:05:02:28:00:12:75:06:02:80:F5 FROM ProgramAlertsCommandEncoder{parameters=ProgramAlertsCommandParameters{configurations=[AlertConfigurationEncoder{slot=7, enabled=true, durationInMinutes=420, autoOff=false, timeTrigger=false, offsetInMinutes=4305, beepType=2, beepRepetition=5}, AlertConfigurationEncoder{slot=2, enabled=true, durationInMinutes=0, autoOff=false, timeTrigger=false, offsetInMinutes=4725, beepType=2, beepRepetition=6}]}, f6666a=[], encoded=[2, 66, 0, 3, -116, 18, 25, 16, 73, 78, 83, 46, 121, -92, 16, -47, 5, 2, 40, 0, 18, 117, 6, 2, -128, -11], headerEncoder=HeaderEncoder{lengthSequenceNumberAndFlags=-29678, f6666a=[], encoded=[2, 66, 0, 3, -116, 18], headerEncoder=null, commandId=0, f6671f=0, commandBodyLength=0}, commandId=25, f6671f=0, commandBodyLength=16} + + I/PodComm: pod command: 024200038C121910494E532E79A410D1050228001275060280F5 + V/PodComm: flags: seqNum=3 ack=false mctf=true + Program Alert: + length:16, number of alerts:2 + ------------------------------------- + alert index: 7 (lump of coal/pod expiration) + enabled: true + duration: 420 minutes + set alarm: false + V/PodComm: type: time - trigger after 4305 minutes (71.75 hrs) + beep type: 2 + beep repetition: 5 + ------------------------------------- + alert index: 2 (imminent pod expiration) + enabled: true + duration: 0 minutes + set alarm: false + type: time - trigger after 4725 minutes (78.75 hrs) + beep type: 2 + beep repetition: 6 + + */ + + /* + Low reservoir + + V/PodComm: *** encode() RESULT FOR HeaderEncoder: 02:42:00:03:20:0C FROM HeaderEncoder{lengthSequenceNumberAndFlags=8204, f6666a=[], encoded=[2, 66, 0, 3, 32, 12], headerEncoder=null, commandId=0, f6671f=0, commandBodyLength=0} + V/PodComm: *** encode() RESULT FOR ProgramAlertsCommandEncoder: 02:42:00:03:20:0C:19:0A:49:4E:53:2E:4C:00:00:C8:01:02:01:49 FROM ProgramAlertsCommandEncoder{parameters=ProgamAlertsCommandParameters{configurations=[AlertConfigurationEncoder{slot=4, enabled=true, durationInMinutes=0, autoOff=false, timeTrigger=true, offsetInMinutes=200, beepType=2, beepRepetition=1}]}, f6666a=[], encoded=[2, 66, 0, 3, 32, 12, 25, 10, 73, 78, 83, 46, 76, 0, 0, -56, 1, 2, 1, 73], headerEncoder=HeaderEncoder{lengthSequenceNumberAndFlags=8204, f6666a=[], encoded=[2, 66, 0, 3, 32, 12], headerEncoder=null, commandId=0, f6671f=0, commandBodyLength=0}, commandId=25, f6671f=0, commandBodyLength=10} + + V/PodComm: flags: seqNum=8 ack=false mctf=false + Program Alert: + length:10, number of alerts:1 + ------------------------------------- + alert index: 4 (low reservoir) + enabled: true + duration: 0 minutes + set alarm: false + type: volume - trigger at 200 micro liter + beep type: 2 + beep repetition: 1 + */ + + /* + User Pod expiration + + V/PodComm: *** encode() CALLED ON ProgramAlertsCommandEncoder + *** encode() CALLED ON HeaderEncoder + V/PodComm: *** encode() RESULT FOR HeaderEncoder: 02:42:00:03:3C:0C FROM HeaderEncoder{lengthSequenceNumberAndFlags=15372, f6666a=[], encoded=[2, 66, 0, 3, 60, 12], headerEncoder=null, commandId=0, f6671f=0, commandBodyLength=0} + V/PodComm: *** encode() RESULT FOR ProgramAlertsCommandEncoder: 02:42:00:03:3C:0C:19:0A:49:4E:53:2E:38:00:0F:EF:03:02:03:E2 FROM ProgramAlertsCommandEncoder{parameters=ProgramAlertsCommandParameters{configurations=[AlertConfigurationEncoder{slot=3, enabled=true, durationInMinutes=0, autoOff=false, timeTrigger=false, offsetInMinutes=4079, beepType=2, beepRepetition=3}]}, f6666a=[], encoded=[2, 66, 0, 3, 60, 12, 25, 10, 73, 78, 83, 46, 56, 0, 15, -17, 3, 2, 3, -30], headerEncoder=HeaderEncoder{lengthSequenceNumberAndFlags=15372, f6666a=[], encoded=[2, 66, 0, 3, 60, 12], headerEncoder=null, commandId=0, f6671f=0, commandBodyLength=0}, commandId=25, f6671f=0, commandBodyLength=10} + + I/PodComm: pod command: 024200033C0C190A494E532E38000FEF030203E2 + V/PodComm: flags: seqNum=15 ack=false mctf=false + Program Alert: + length:10, number of alerts:1 + ------------------------------------- + alert index: 3 (user pod expiration) + enabled: true + duration: 0 minutes + set alarm: false + type: time - trigger after 4079 minutes (67.98 hrs) + beep type: 2 + beep repetition: 3 + */ + + + /* + Lump of coal + + V/PodComm: *** encode() CALLED ON ProgramAlertsCommandEncoder + *** encode() CALLED ON HeaderEncoder + *** encode() RESULT FOR HeaderEncoder: 02:42:00:03:28:0C FROM HeaderEncoder{lengthSequenceNumberAndFlags=10252, f6666a=[], encoded=[2, 66, 0, 3, 40, 12], headerEncoder=null, commandId=0, f6671f=0, commandBodyLength=0} + D/MainActivity: Pod Activation 1: got Pod Event + V/PodComm: *** encode() RESULT FOR ProgramAlertsCommandEncoder: 02:42:00:03:28:0C:19:0A:49:4E:53:2E:78:37:00:05:08:02:03:56 FROM ProgramAlertsCommandEncoder{parameters=ProgramAlertsCommandParameters{configurations=[AlertConfigurationEncoder{slot=7, enabled=true, durationInMinutes=55, autoOff=false, timeTrigger=false, offsetInMinutes=5, beepType=2, beepRepetition=8}]}, f6666a=[], encoded=[2, 66, 0, 3, 40, 12, 25, 10, 73, 78, 83, 46, 120, 55, 0, 5, 8, 2, 3, 86], headerEncoder=HeaderEncoder{lengthSequenceNumberAndFlags=10252, f6666a=[], encoded=[2, 66, 0, 3, 40, 12], headerEncoder=null, commandId=0, f6671f=0, commandBodyLength=0}, commandId=25, f6671f=0, commandBodyLength=10} + + V/PodComm: flags: seqNum=10 ack=false mctf=false + Program Alert: + length:10, number of alerts:1 + ------------------------------------- + V/PodComm: alert index: 7 (lump of coal/pod expiration) + enabled: true + duration: 55 minutes + set alarm: false + type: time - trigger after 5 minutes (0.08 hrs) + beep type: 2 + beep repetition: 8 + */ +} diff --git a/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/SetUniqueIdCommandTest.java b/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/SetUniqueIdCommandTest.java new file mode 100644 index 0000000000..055f215e5d --- /dev/null +++ b/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/SetUniqueIdCommandTest.java @@ -0,0 +1,19 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command; + +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; +import org.junit.Test; + +import java.util.Date; + +import static org.junit.Assert.assertArrayEquals; + +public class SetUniqueIdCommandTest { + @Test + public void testEncoding() throws DecoderException { + byte[] encoded = new SetUniqueIdCommand(37879811, (short) 6, 135556289, 681767, new Date(2021, 1, 10, 14, 41), false) // + .getEncoded(); + + assertArrayEquals(Hex.decodeHex("FFFFFFFF18150313024200031404020A150E2908146CC1000A67278344"), encoded); + } +} \ No newline at end of file