diff --git a/omnipod-common/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/common/queue/command/CommandDisableSuspendAlerts.kt b/omnipod-common/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/common/queue/command/CommandDisableSuspendAlerts.kt new file mode 100644 index 0000000000..384c115d27 --- /dev/null +++ b/omnipod-common/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/common/queue/command/CommandDisableSuspendAlerts.kt @@ -0,0 +1,9 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command + +import info.nightscout.androidaps.queue.commands.CustomCommand + +class CommandDisableSuspendAlerts: CustomCommand { + + override val statusDescription: String + get() = "DISABLE SUSPEND ALERTS" +} diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt index 1baa37737e..d8c998c90a 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt @@ -502,7 +502,10 @@ class OmnipodDashPumpPlugin @Inject constructor( // prevent setBasal requests return true } - // TODO: what do we have to answer here if delivery is suspended? + if (podStateManager.isSuspended) { + // set new basal profile failed midway + return false + } val running = podStateManager.basalProgram val equal = (mapProfileToBasalProgram(profile) == running) aapsLogger.info(LTag.PUMP, "set: $equal. profile=$profile, running=$running") @@ -1089,8 +1092,6 @@ class OmnipodDashPumpPlugin @Inject constructor( return when (customCommand) { is CommandSilenceAlerts -> silenceAlerts() - is CommandSuspendDelivery -> - suspendDelivery() is CommandResumeDelivery -> resumeDelivery() is CommandDeactivatePod -> @@ -1101,7 +1102,8 @@ class OmnipodDashPumpPlugin @Inject constructor( updateAlertConfiguration() is CommandPlayTestBeep -> playTestBeep() - + is CommandDisableSuspendAlerts -> + disableSuspendAlerts() else -> { aapsLogger.warn(LTag.PUMP, "Unsupported custom command: " + customCommand.javaClass.name) PumpEnactResult(injector).success(false).enacted(false).comment( @@ -1124,27 +1126,28 @@ class OmnipodDashPumpPlugin @Inject constructor( } ?: PumpEnactResult(injector).success(false).enacted(false).comment("No active alerts") // TODO i18n } - private fun suspendDelivery(): PumpEnactResult { - return executeProgrammingCommand( - historyEntry = history.createRecord(OmnipodCommandType.SUSPEND_DELIVERY), - command = omnipodManager.suspendDelivery(hasBasalBeepEnabled()) - .filter { podEvent -> podEvent.isCommandSent() } - .map { - pumpSyncTempBasal( - 0.0, - PodConstants.MAX_POD_LIFETIME.toMinutes(), - PumpSync.TemporaryBasalType.PUMP_SUSPEND - ) - } - .ignoreElements(), - pre = observeDeliveryActive(), - ).doFinally { - notifyOnUnconfirmed( - Notification.PUMP_ERROR, - "Unconfirmed suspendDelivery command. Please refresh pod status", - R.raw.boluserror - ) - }.toPumpEnactResult() + private fun disableSuspendAlerts(): PumpEnactResult { + val alerts = listOf( + AlertConfiguration( + AlertType.SUSPEND_ENDED, + enabled = false, + durationInMinutes = 0, + autoOff = false, + AlertTrigger.TimerTrigger( + 0 + ), + BeepType.FOUR_TIMES_BIP_BEEP, + BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN + ), + ) + val ret = executeProgrammingCommand( + historyEntry = history.createRecord(OmnipodCommandType.CONFIGURE_ALERTS), + command = omnipodManager.programAlerts(alerts).ignoreElements(), + ).toPumpEnactResult() + if (ret.success && ret.enacted) { + podStateManager.suspendAlertsEnabled = false + } + return ret } private fun observeDeliveryActive(): Completable = Completable.defer { @@ -1249,7 +1252,7 @@ class OmnipodDashPumpPlugin @Inject constructor( expiryAlertDelay.toMinutes().toShort() ), BeepType.FOUR_TIMES_BIP_BEEP, - BeepRepetitionType.XXX2 + BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN ) ) return executeProgrammingCommand( @@ -1369,9 +1372,11 @@ class OmnipodDashPumpPlugin @Inject constructor( ) podStateManager.tempBasal = null rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED)) + rxBus.send(EventDismissNotification(Notification.FAILED_UPDATE_PROFILE)) + rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS)) + rxBus.send(EventDismissNotification(Notification.OMNIPOD_TIME_OUT_OF_SYNC)) + commandQueue.customCommand(CommandDisableSuspendAlerts(), null) } - rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS)) - rxBus.send(EventDismissNotification(Notification.OMNIPOD_TIME_OUT_OF_SYNC)) } OmnipodCommandType.SET_BASAL_PROFILE -> { @@ -1394,6 +1399,7 @@ class OmnipodDashPumpPlugin @Inject constructor( rxBus.send(EventDismissNotification(Notification.FAILED_UPDATE_PROFILE)) rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS)) rxBus.send(EventDismissNotification(Notification.OMNIPOD_TIME_OUT_OF_SYNC)) + commandQueue.customCommand(CommandDisableSuspendAlerts(), null) } } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt index 29ddc58221..bc3fa572b1 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt @@ -439,7 +439,7 @@ class OmnipodDashManagerImpl @Inject constructor( userExpiryAlertDelay.toMinutes().toShort() ), BeepType.FOUR_TIMES_BIP_BEEP, - BeepRepetitionType.XXX2 + BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN ) ) } @@ -504,8 +504,29 @@ class OmnipodDashManagerImpl @Inject constructor( return Observable.concat( observePodRunning, observeConnectToPod, - observeSendStopDeliveryCommand(StopDeliveryCommand.DeliveryType.ALL, hasBasalBeepEnabled) - ).interceptPodEvents() + observeSuspendDeliveryCommand(hasBasalBeepEnabled) + ).doOnComplete { + podStateManager.suspendAlertsEnabled = true + }.interceptPodEvents() + } + + private fun observeSuspendDeliveryCommand(hasBasalBeepEnabled: Boolean): Observable { + return Observable.defer { + val beepType = if (!hasBasalBeepEnabled) + BeepType.SILENT + else + BeepType.LONG_SINGLE_BEEP + + bleManager.sendCommand( + SuspendDeliveryCommand.Builder() + .setSequenceNumber(podStateManager.messageSequenceNumber) + .setUniqueId(podStateManager.uniqueId!!.toInt()) + .setNonce(NONCE) + .setBeepType(beepType) + .build(), + DefaultStatusResponse::class + ) + } } private fun observeSendProgramTempBasalCommand(rate: Double, durationInMinutes: Short, tempBasalBeeps: Boolean): Observable { diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/ProgramAlertsCommand.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/ProgramAlertsCommand.kt index b13ed65498..21b041b953 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/ProgramAlertsCommand.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/ProgramAlertsCommand.kt @@ -38,6 +38,18 @@ class ProgramAlertsCommand private constructor( return appendCrc(byteBuffer.array()) } + val encodedWithoutHeaderAndCRC32: ByteArray + get() { + val byteBuffer: ByteBuffer = ByteBuffer.allocate(getLength().toInt()) + .put(commandType.value) + .put(getBodyLength()) + .putInt(nonce) + for (configuration in alertConfigurations) { + byteBuffer.put(configuration.encoded) + } + return byteBuffer.array() + } + override fun toString(): String { return "ProgramAlertsCommand{" + "alertConfigurations=" + alertConfigurations + diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/SuspendDeliveryCommand.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/SuspendDeliveryCommand.kt new file mode 100644 index 0000000000..23409a812e --- /dev/null +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/SuspendDeliveryCommand.kt @@ -0,0 +1,113 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command + +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.* +import java.nio.ByteBuffer +import java.util.* + +// StopDelivery.ALL followed by ProgramAlerts +class SuspendDeliveryCommand private constructor( + uniqueId: Int, + sequenceNumber: Short, + multiCommandFlag: Boolean, + private val beepType: BeepType, + nonce: Int +) : NonceEnabledCommand(CommandType.STOP_DELIVERY, uniqueId, sequenceNumber, multiCommandFlag, nonce) { + + override val encoded: ByteArray + get() { + val alerts = listOf( + AlertConfiguration( + AlertType.SUSPEND_ENDED, + enabled = true, + durationInMinutes = 0, + autoOff = false, + AlertTrigger.TimerTrigger( + 20 + ), + BeepType.FOUR_TIMES_BIP_BEEP, + BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN + ), + ) + val programAlerts = ProgramAlertsCommand.Builder() + .setNonce(nonce) + .setUniqueId(uniqueId) + .setAlertConfigurations(alerts) + .setSequenceNumber(sequenceNumber) + .setMultiCommandFlag(false) + .build() + .encodedWithoutHeaderAndCRC32 + + val byteBuffer = ByteBuffer.allocate(LENGTH + HEADER_LENGTH + programAlerts.size) + .put(encodeHeader(uniqueId, sequenceNumber, (LENGTH + programAlerts.size).toShort(), multiCommandFlag)) + .put(commandType.value) + .put(BODY_LENGTH) + .putInt(nonce) + .put((beepType.value.toInt() shl 4 or DeliveryType.ALL.encoded[0].toInt()).toByte()) + .put(programAlerts) + .array() + + return appendCrc( + byteBuffer + ) + } + + override fun toString(): String { + return "SuspendDeliveryCommand{" + + "deliveryType=" + DeliveryType.ALL + + ", beepType=" + beepType + + ", nonce=" + nonce + + ", commandType=" + commandType + + ", uniqueId=" + uniqueId + + ", sequenceNumber=" + sequenceNumber + + ", multiCommandFlag=" + multiCommandFlag + + '}' + } + + enum class DeliveryType( + private val basal: Boolean, + private val tempBasal: Boolean, + private val bolus: Boolean + ) : Encodable { + + BASAL(true, false, false), TEMP_BASAL(false, true, false), BOLUS(false, false, true), ALL(true, true, true); + + override val encoded: ByteArray + get() { + val bitSet = BitSet(8) + bitSet[0] = basal + bitSet[1] = tempBasal + bitSet[2] = bolus + return bitSet.toByteArray() + } + } + + class Builder : NonceEnabledCommandBuilder() { + private var beepType: BeepType? = BeepType.LONG_SINGLE_BEEP + + fun setBeepType(beepType: BeepType): Builder { + this.beepType = beepType + return this + } + + override fun buildCommand(): SuspendDeliveryCommand { + requireNotNull(beepType) { "beepType can not be null" } + + return SuspendDeliveryCommand( + uniqueId!!, + sequenceNumber!!, + multiCommandFlag, + beepType!!, + nonce!! + ) + } + } + + companion object { + + private const val LENGTH: Short = 7 + private const val BODY_LENGTH: Byte = 5 + } +} diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/BeepRepetitionType.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/BeepRepetitionType.kt index cf668aa036..1e290b9e3f 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/BeepRepetitionType.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/BeepRepetitionType.kt @@ -6,8 +6,8 @@ enum class BeepRepetitionType( ) { XXX(0x01.toByte()), // Used in lump of coal alert, LOW_RESERVOIR - XXX2(0x03.toByte()), // Used in USER_SET_EXPIRATION + EVERY_MINUTE_AND_EVERY_15_MIN(0x03.toByte()), // Used in USER_SET_EXPIRATION, suspend delivery XXX3(0x05.toByte()), // published system expiration alert - XXX4(0x06.toByte()), // Used in imminent pod expiration alert, suspend in progress + XXX4(0x06.toByte()), // Used in imminent pod expiration alert, suspend in progress. No repeat? XXX5(0x08.toByte()); // Lump of coal alert } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManager.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManager.kt index fa38444317..c3efc76604 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManager.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManager.kt @@ -77,6 +77,7 @@ interface OmnipodDashPodStateManager { var basalProgram: BasalProgram? val activeCommand: ActiveCommand? val lastBolus: LastBolus? + var suspendAlertsEnabled: Boolean fun increaseMessageSequenceNumber() fun increaseEapAkaSequenceNumber(): ByteArray diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt index 9c3fcb3454..fb3a8adcfb 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt @@ -225,6 +225,13 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( store() } + override var suspendAlertsEnabled: Boolean + get() = podState.suspendAlertsEnabled + set(enabled) { + podState.suspendAlertsEnabled = enabled + store() + } + override val lastStatusResponseReceived: Long get() = podState.lastStatusResponseReceived @@ -714,6 +721,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( var timeZoneOffset: Int? = null var timeZoneUpdated: Long? = null var alarmSynced: Boolean = false + var suspendAlertsEnabled: Boolean = false var bleVersion: SoftwareVersion? = null var firmwareVersion: SoftwareVersion? = null diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt index e7c5917015..60a6f928a7 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt @@ -439,6 +439,10 @@ class OmnipodDashOverviewFragment : DaggerFragment() { R.string.omnipod_common_alert_expiration_advisory AlertType.AUTO_OFF -> R.string.omnipod_common_alert_shutdown_imminent + AlertType.SUSPEND_IN_PROGRESS -> + R.string.omnipod_common_alert_delivery_suspended + AlertType.SUSPEND_ENDED -> + R.string.omnipod_common_alert_delivery_suspended else -> R.string.omnipod_common_alert_unknown_alert } @@ -629,16 +633,8 @@ class OmnipodDashOverviewFragment : DaggerFragment() { private fun updateSuspendDeliveryButton() { // If the Pod is currently suspended, we show the Resume delivery button instead. - if (isSuspendDeliveryButtonEnabled() && - podStateManager.isPodRunning && - (!podStateManager.isSuspended || commandQueue.isCustomCommandInQueue(CommandSuspendDelivery::class.java)) - ) { - buttonBinding.buttonSuspendDelivery.visibility = View.VISIBLE - buttonBinding.buttonSuspendDelivery.isEnabled = - podStateManager.isPodRunning && !podStateManager.isSuspended && isQueueEmpty() - } else { - buttonBinding.buttonSuspendDelivery.visibility = View.GONE - } + // disable the 'suspendDelivery' button. + buttonBinding.buttonSuspendDelivery.visibility = View.GONE } private fun updateSetTimeButton() { @@ -654,10 +650,6 @@ class OmnipodDashOverviewFragment : DaggerFragment() { return sp.getBoolean(R.string.omnipod_common_preferences_automatically_silence_alerts, false) } - private fun isSuspendDeliveryButtonEnabled(): Boolean { - return sp.getBoolean(R.string.key_omnipod_common_suspend_delivery_button_enabled, false) - } - private fun displayErrorDialog(title: String, message: String, withSound: Boolean) { context?.let { ErrorHelperActivity.runAlarm(it, message, title, if (withSound) R.raw.boluserror else 0) diff --git a/omnipod-dash/src/main/res/values/strings.xml b/omnipod-dash/src/main/res/values/strings.xml index 607f6ebdc5..f16cd9b2de 100644 --- a/omnipod-dash/src/main/res/values/strings.xml +++ b/omnipod-dash/src/main/res/values/strings.xml @@ -47,4 +47,5 @@ Rate: %1$.2f U, duration: %2$d minutes %1$.2f U Delivering %1$.2f U + Insulin delivery is suspended diff --git a/omnipod-dash/src/main/res/xml/omnipod_dash_preferences.xml b/omnipod-dash/src/main/res/xml/omnipod_dash_preferences.xml index 0e812a9709..e53a64c3ac 100644 --- a/omnipod-dash/src/main/res/xml/omnipod_dash_preferences.xml +++ b/omnipod-dash/src/main/res/xml/omnipod_dash_preferences.xml @@ -104,12 +104,7 @@ android:key="@string/key_common_preferences_category_other_settings" android:title="@string/omnipod_common_preferences_category_other" app:initialExpandedChildrenCount="0"> - - - +