diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/OmnipodPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/OmnipodPumpPlugin.java index 372a6f9872..807225a860 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/OmnipodPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/OmnipodPumpPlugin.java @@ -173,6 +173,10 @@ public class OmnipodPumpPlugin extends PumpPluginAbstract implements OmnipodPump public void initPumpStatusData() { this.pumpStatusLocal = new OmnipodPumpStatus(pumpDescription); + if (omnipodCommunicationManager != null) { + omnipodCommunicationManager.setPumpStatus(pumpStatusLocal); + } + OmnipodUtil.setPumpStatus(pumpStatusLocal); pumpStatusLocal.lastConnection = SP.getLong(RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L); @@ -493,6 +497,9 @@ public class OmnipodPumpPlugin extends PumpPluginAbstract implements OmnipodPump if (isLoggingEnabled()) LOG.warn("!!!! Reset Pump Status Local"); pumpStatusLocal = OmnipodUtil.getPumpStatus(); + if (omnipodCommunicationManager != null) { + omnipodCommunicationManager.setPumpStatus(pumpStatusLocal); + } } return pumpStatusLocal; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/comm/action/service/InsertCannulaService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/comm/action/service/InsertCannulaService.java index 703a252b8e..2df5305342 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/comm/action/service/InsertCannulaService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/comm/action/service/InsertCannulaService.java @@ -26,6 +26,9 @@ public class InsertCannulaService { public StatusResponse executeExpirationRemindersAlertCommand(OmnipodCommunicationService communicationService, PodSessionState podState) { + + AlertConfiguration lowReservoirAlertConfiguration = AlertConfigurationFactory.createLowReservoirAlertConfiguration(OmnipodConst.LOW_RESERVOIR_ALERT); + DateTime endOfServiceTime = podState.getActivatedAt().plus(OmnipodConst.SERVICE_DURATION); Duration timeUntilExpirationAdvisoryAlarm = new Duration(DateTime.now(), @@ -41,6 +44,7 @@ public class InsertCannulaService { false, Duration.ZERO); List alertConfigurations = Arrays.asList( // + lowReservoirAlertConfiguration, // expirationAdvisoryAlertConfiguration, // shutdownImminentAlertConfiguration, // autoOffAlertConfiguration // diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/comm/message/response/StatusResponse.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/comm/message/response/StatusResponse.java index 8a2e64fdd3..a9d831f8ae 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/comm/message/response/StatusResponse.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/comm/message/response/StatusResponse.java @@ -20,7 +20,7 @@ public class StatusResponse extends MessageBlock { private final PodProgressStatus podProgressStatus; private final Duration timeActive; private final Double reservoirLevel; - private final double insulin; + private final double insulinDelivered; private final double insulinNotDelivered; private final byte podMessageCounter; private final AlertSet alerts; @@ -40,7 +40,7 @@ public class StatusResponse extends MessageBlock { int highInsulinBits = (encodedData[2] & 0xF) << 9; int middleInsulinBits = ByteUtil.convertUnsignedByteToInt(encodedData[3]) << 1; int lowInsulinBits = ByteUtil.convertUnsignedByteToInt(encodedData[4]) >>> 7; - this.insulin = OmnipodConst.POD_PULSE_SIZE * (highInsulinBits | middleInsulinBits | lowInsulinBits); + this.insulinDelivered = OmnipodConst.POD_PULSE_SIZE * (highInsulinBits | middleInsulinBits | lowInsulinBits); this.podMessageCounter = (byte) ((encodedData[4] >>> 3) & 0xf); this.insulinNotDelivered = OmnipodConst.POD_PULSE_SIZE * (((encodedData[4] & 0x03) << 8) | ByteUtil.convertUnsignedByteToInt(encodedData[5])); @@ -75,8 +75,8 @@ public class StatusResponse extends MessageBlock { return reservoirLevel; } - public double getInsulin() { - return insulin; + public double getInsulinDelivered() { + return insulinDelivered; } public double getInsulinNotDelivered() { @@ -110,7 +110,7 @@ public class StatusResponse extends MessageBlock { ", podProgressStatus=" + podProgressStatus + ", timeActive=" + timeActive + ", reservoirLevel=" + reservoirLevel + - ", insulin=" + insulin + + ", insulinDelivered=" + insulinDelivered + ", insulinNotDelivered=" + insulinNotDelivered + ", podMessageCounter=" + podMessageCounter + ", alerts=" + alerts + diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/defs/AlertConfigurationFactory.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/defs/AlertConfigurationFactory.java index 8ff303a332..de6bcb9f4b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/defs/AlertConfigurationFactory.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/defs/AlertConfigurationFactory.java @@ -3,6 +3,11 @@ package info.nightscout.androidaps.plugins.pump.omnipod.defs; import org.joda.time.Duration; public class AlertConfigurationFactory { + public static AlertConfiguration createLowReservoirAlertConfiguration(Double units) { + return new AlertConfiguration(AlertType.LOW_RESERVOIR_ALERT, AlertSlot.SLOT4, true, false, Duration.ZERO, + new UnitsRemainingAlertTrigger(units), BeepType.BIP_BEEP_BIP_BEEP_BIP_BEEP_BIP_BEEP, BeepRepeat.EVERY_15_MINUTES); + } + public static AlertConfiguration createExpirationAdvisoryAlertConfiguration(Duration timeUntilAlert, Duration duration) { return new AlertConfiguration(AlertType.EXPIRATION_ADVISORY_ALERT, AlertSlot.SLOT7, true, false, duration, new TimerAlertTrigger(timeUntilAlert), BeepType.BIP_BEEP_BIP_BEEP_BIP_BEEP_BIP_BEEP, BeepRepeat.EVERY_60_MINUTES); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/defs/AlertType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/defs/AlertType.java index 085652f6d6..eb4b068a6f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/defs/AlertType.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/defs/AlertType.java @@ -1,11 +1,11 @@ package info.nightscout.androidaps.plugins.pump.omnipod.defs; public enum AlertType { - WAITING_FOR_PAIRING_REMINDER, + FINISH_PAIRING_REMINDER, FINISH_SETUP_REMINDER, EXPIRATION_ALERT, EXPIRATION_ADVISORY_ALERT, SHUTDOWN_IMMINENT_ALARM, - LOW_RESERVOIC_ALERT, + LOW_RESERVOIR_ALERT, AUTO_OFF_ALARM } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/comm/AapsOmnipodManager.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/comm/AapsOmnipodManager.java index fdd8787806..176d9c71f7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/comm/AapsOmnipodManager.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/comm/AapsOmnipodManager.java @@ -1,5 +1,8 @@ package info.nightscout.androidaps.plugins.pump.omnipod.driver.comm; +import android.text.TextUtils; + +import org.jetbrains.annotations.NotNull; import org.joda.time.DateTime; import org.joda.time.Duration; import org.slf4j.Logger; @@ -12,6 +15,7 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.PumpEnactResult; +import info.nightscout.androidaps.events.Event; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress; @@ -21,6 +25,8 @@ import info.nightscout.androidaps.plugins.pump.omnipod.comm.OmnipodManager; import info.nightscout.androidaps.plugins.pump.omnipod.comm.SetupActionResult; import info.nightscout.androidaps.plugins.pump.omnipod.comm.message.response.StatusResponse; import info.nightscout.androidaps.plugins.pump.omnipod.comm.message.response.podinfo.PodInfoResponse; +import info.nightscout.androidaps.plugins.pump.omnipod.defs.AlertSlot; +import info.nightscout.androidaps.plugins.pump.omnipod.defs.AlertType; import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodCommunicationManagerInterface; import info.nightscout.androidaps.plugins.pump.omnipod.defs.PodInfoType; import info.nightscout.androidaps.plugins.pump.omnipod.defs.PodInitActionType; @@ -30,6 +36,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.defs.schedule.BasalSchedu import info.nightscout.androidaps.plugins.pump.omnipod.defs.state.PodSessionState; import info.nightscout.androidaps.plugins.pump.omnipod.driver.OmnipodPumpStatus; import info.nightscout.androidaps.plugins.pump.omnipod.driver.db.PodDbEntryType; +import info.nightscout.androidaps.plugins.pump.omnipod.events.EventOmnipodAcknowledgeAlertsChanged; import info.nightscout.androidaps.plugins.pump.omnipod.exception.ActionInitializationException; import info.nightscout.androidaps.plugins.pump.omnipod.exception.CommandInitializationException; import info.nightscout.androidaps.plugins.pump.omnipod.exception.CommunicationException; @@ -62,13 +69,43 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface public AapsOmnipodManager(OmnipodCommunicationService communicationService, PodSessionState podState, OmnipodPumpStatus pumpStatus) { delegate = new OmnipodManager(communicationService, podState, podSessionState -> { + // Handle pod state changes + OmnipodUtil.setPodSessionState(podSessionState); - // TODO update active alerts (and other stuff(?)) in UI + + if (pumpStatus != null) { + if (podSessionState.hasActiveAlerts()) { + List alerts = translateActiveAlerts(podSessionState); + String alertsText = TextUtils.join("\n", alerts); + + if (!pumpStatus.ackAlertsAvailable || !alertsText.equals(pumpStatus.ackAlertsText)) { + pumpStatus.ackAlertsAvailable = true; + pumpStatus.ackAlertsText = TextUtils.join("\n", alerts); + + sendEvent(new EventOmnipodAcknowledgeAlertsChanged()); + } + } else { + if (pumpStatus.ackAlertsAvailable) { + pumpStatus.ackAlertsAvailable = false; + + sendEvent(new EventOmnipodAcknowledgeAlertsChanged()); + } + } + } }); this.pumpStatus = pumpStatus; instance = this; } + @NotNull + private List translateActiveAlerts(PodSessionState podSessionState) { + List alerts = new ArrayList<>(); + for (AlertSlot alertSlot : podSessionState.getActiveAlerts().getAlertSlots()) { + alerts.add(translateAlertType(podSessionState.getConfiguredAlertType(alertSlot))); + } + return alerts; + } + @Override public PumpEnactResult initPod(PodInitActionType podInitActionType, PodInitReceiver podInitReceiver, Profile profile) { if (PodInitActionType.PairAndPrimeWizardStep.equals(podInitActionType)) { @@ -159,7 +196,7 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface EventOverviewBolusProgress progressUpdateEvent = EventOverviewBolusProgress.INSTANCE; progressUpdateEvent.setStatus(getStringResource(R.string.bolusdelivering, units)); progressUpdateEvent.setPercent(percentage); - RxBus.INSTANCE.send(progressUpdateEvent); + sendEvent(progressUpdateEvent); }); } catch (Exception ex) { String comment = handleAndTranslateException(ex); @@ -279,6 +316,7 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface } // TODO should we add this to the OmnipodCommunicationManager interface? + // Updates the pods current time based on the device timezone and the pod's time zone public PumpEnactResult setTime() { try { // CAUTION cancels TBR @@ -383,6 +421,32 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface return comment; } + private void sendEvent(Event event) { + RxBus.INSTANCE.send(event); + } + + private String translateAlertType(AlertType alertType) { + if (alertType == null) { + return getStringResource(R.string.omnipod_alert_unknown_alert); + } + switch (alertType) { + case FINISH_PAIRING_REMINDER: + return getStringResource(R.string.omnipod_alert_finish_pairing_reminder); + case FINISH_SETUP_REMINDER: + return getStringResource(R.string.omnipod_alert_finish_setup_reminder_reminder); + case EXPIRATION_ALERT: + return getStringResource(R.string.omnipod_alert_expiration); + case EXPIRATION_ADVISORY_ALERT: + return getStringResource(R.string.omnipod_alert_expiration_advisory); + case SHUTDOWN_IMMINENT_ALARM: + return getStringResource(R.string.omnipod_alert_shutdown_imminent); + case LOW_RESERVOIR_ALERT: + return getStringResource(R.string.omnipod_alert_low_reservoir); + default: + return alertType.name(); + } + } + private boolean isBolusBeepsEnabled() { // TODO return true; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/util/OmnipodConst.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/util/OmnipodConst.java index 06713f4d75..33206e0a30 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/util/OmnipodConst.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/util/OmnipodConst.java @@ -41,6 +41,7 @@ public class OmnipodConst { public static final Duration EXPIRATION_ADVISORY_WINDOW = Duration.standardHours(2); public static final Duration END_OF_SERVICE_IMMINENT_WINDOW = Duration.standardHours(1); public static final Duration NOMINAL_POD_LIFE = SERVICE_DURATION.minus(END_OF_SERVICE_IMMINENT_WINDOW).minus(EXPIRATION_ADVISORY_WINDOW); + public static final double LOW_RESERVOIR_ALERT = 20.0; public static final double POD_PRIME_BOLUS_UNITS = 2.6; public static final double POD_CANNULA_INSERTION_BOLUS_UNITS = 0.5; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d0ba04ca75..6724a5c868 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1727,6 +1727,13 @@ Omnipod Dash DASH Pump integration for Omnipod Dash. + Finish pairing reminder + Finish setup reminder + Pod wil expire soon + Pod will expire soon + Shutdown is imminent + Low reservoir + Unknown alert diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/comm/message/command/ConfigureAlertsCommandTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/comm/message/command/ConfigureAlertsCommandTest.java index bfe78a1765..a7c1579635 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/comm/message/command/ConfigureAlertsCommandTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/comm/message/command/ConfigureAlertsCommandTest.java @@ -4,6 +4,7 @@ import org.joda.time.Duration; import org.junit.Test; import java.util.Arrays; +import java.util.Collections; import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; import info.nightscout.androidaps.plugins.pump.omnipod.defs.AlertConfiguration; @@ -12,6 +13,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.defs.AlertType; import info.nightscout.androidaps.plugins.pump.omnipod.defs.BeepRepeat; import info.nightscout.androidaps.plugins.pump.omnipod.defs.BeepType; import info.nightscout.androidaps.plugins.pump.omnipod.defs.TimerAlertTrigger; +import info.nightscout.androidaps.plugins.pump.omnipod.defs.UnitsRemainingAlertTrigger; import static org.junit.Assert.assertArrayEquals; @@ -73,5 +75,24 @@ public class ConfigureAlertsCommandTest { configureAlertsCommand.getRawData()); } - // TODO add tests + @Test + public void testLowReservoirAlert() { + AlertConfiguration alertConfiguration = new AlertConfiguration(// + AlertType.LOW_RESERVOIR_ALERT, // + AlertSlot.SLOT4, // + true, // + false, // + Duration.ZERO, // + new UnitsRemainingAlertTrigger(10.0), // + BeepType.BEEP_BEEP_BEEP_BEEP, // + BeepRepeat.EVERY_MINUTE_FOR_15_MINUTES); + + ConfigureAlertsCommand configureAlertsCommand = new ConfigureAlertsCommand( // + 0xae01a66c, // + Collections.singletonList(alertConfiguration)); + + assertArrayEquals( + ByteUtil.fromHexString("190aae01a66c4c0000640102"), // + configureAlertsCommand.getRawData()); + } } diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/comm/message/response/StatusResponseTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/comm/message/response/StatusResponseTest.java index 2c74ed1f74..d5a79e9ffd 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/comm/message/response/StatusResponseTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/comm/message/response/StatusResponseTest.java @@ -4,6 +4,7 @@ import org.joda.time.Duration; import org.junit.Test; import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.omnipod.defs.AlertSlot; import info.nightscout.androidaps.plugins.pump.omnipod.defs.DeliveryStatus; import info.nightscout.androidaps.plugins.pump.omnipod.defs.PodProgressStatus; import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodConst; @@ -44,7 +45,7 @@ public class StatusResponseTest { assertEquals(PodProgressStatus.RUNNING_ABOVE_FIFTY_UNITS, statusResponse.getPodProgressStatus()); assertNull("Reservoir level should be null", statusResponse.getReservoirLevel()); assertEquals(Duration.standardMinutes(1307).getMillis(), statusResponse.getTimeActive().getMillis()); - assertEquals(60.05, statusResponse.getInsulin(), 0.000001); + assertEquals(60.05, statusResponse.getInsulinDelivered(), 0.000001); assertEquals(15, statusResponse.getPodMessageCounter()); assertEquals(0, statusResponse.getInsulinNotDelivered(), 0.000001); assertEquals(0, statusResponse.getAlerts().getAlertSlots().size()); @@ -52,30 +53,52 @@ public class StatusResponseTest { assertArrayEquals(ByteUtil.fromHexString("1d180258f80000146fff"), statusResponse.getRawData()); } + @Test + public void testWithSampleCaptureWithReplacePodSoonAlert() { + byte[] bytes = ByteUtil.fromHexString("1d19061f6800044295e8"); // From https://github.com/openaps/openomni/wiki/Status-Response-1D-long-run-%28Lytrix%29 + StatusResponse statusResponse = new StatusResponse(bytes); + + assertEquals(DeliveryStatus.NORMAL, statusResponse.getDeliveryStatus()); + assertEquals(PodProgressStatus.RUNNING_BELOW_FIFTY_UNITS, statusResponse.getPodProgressStatus()); + assertEquals(24.4, statusResponse.getReservoirLevel(), 0.000001); + assertEquals(Duration.standardMinutes(4261).getMillis(), statusResponse.getTimeActive().getMillis()); + assertEquals(156.7, statusResponse.getInsulinDelivered(), 0.000001); + assertEquals(13, statusResponse.getPodMessageCounter()); + assertEquals(0, statusResponse.getInsulinNotDelivered(), 0.000001); + assertEquals(1, statusResponse.getAlerts().getAlertSlots().size()); + assertEquals(AlertSlot.SLOT3, statusResponse.getAlerts().getAlertSlots().get(0)); + + assertArrayEquals(ByteUtil.fromHexString("1d19061f6800044295e8"), statusResponse.getRawData()); + } + @Test public void testLargeValues() { - byte[] bytes = ByteUtil.fromHexString("0011ffffffffffffffffff"); + byte[] bytes = ByteUtil.fromHexString("1d11ffffffffffffffff"); StatusResponse statusResponse = new StatusResponse(bytes); assertEquals(Duration.standardMinutes(8191).getMillis(), statusResponse.getTimeActive().getMillis()); assertEquals(OmnipodConst.POD_PULSE_SIZE * 1023, statusResponse.getInsulinNotDelivered(), 0.000001); assertNull("Reservoir level should be null", statusResponse.getReservoirLevel()); - assertEquals(OmnipodConst.POD_PULSE_SIZE * 8191, statusResponse.getInsulin(), 0.0000001); + assertEquals(OmnipodConst.POD_PULSE_SIZE * 8191, statusResponse.getInsulinDelivered(), 0.0000001); assertEquals(15, statusResponse.getPodMessageCounter()); assertEquals(8, statusResponse.getAlerts().getAlertSlots().size()); + + assertArrayEquals(ByteUtil.fromHexString("1d11ffffffffffffffff"), statusResponse.getRawData()); } @Test public void testWithReservoirLevel() { - byte[] bytes = ByteUtil.fromHexString("1d19050ec82c08376f9801dc"); + byte[] bytes = ByteUtil.fromHexString("1d19050ec82c08376f98"); StatusResponse statusResponse = new StatusResponse(bytes); assertTrue(Duration.standardMinutes(3547).isEqual(statusResponse.getTimeActive())); assertEquals(DeliveryStatus.NORMAL, statusResponse.getDeliveryStatus()); assertEquals(PodProgressStatus.RUNNING_BELOW_FIFTY_UNITS, statusResponse.getPodProgressStatus()); - assertEquals(129.45, statusResponse.getInsulin(), 0.00001); + assertEquals(129.45, statusResponse.getInsulinDelivered(), 0.00001); assertEquals(46.00, statusResponse.getReservoirLevel(), 0.00001); assertEquals(2.2, statusResponse.getInsulinNotDelivered(), 0.0001); assertEquals(9, statusResponse.getPodMessageCounter()); + + assertArrayEquals(ByteUtil.fromHexString("1d19050ec82c08376f98"), statusResponse.getRawData()); } } \ No newline at end of file