Merge pull request #984 from 0pen-dash/avereha/suspend-beep

dash: suspend alerts
This commit is contained in:
Milos Kozak 2021-11-25 07:55:50 +01:00 committed by GitHub
commit da623e92d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 232 additions and 54 deletions

View file

@ -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"
}

View file

@ -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
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
}
.ignoreElements(),
pre = observeDeliveryActive(),
).doFinally {
notifyOnUnconfirmed(
Notification.PUMP_ERROR,
"Unconfirmed suspendDelivery command. Please refresh pod status",
R.raw.boluserror
)
}.toPumpEnactResult()
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)
}
}
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)
}
}

View file

@ -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<PodEvent> {
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<PodEvent> {

View file

@ -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 +

View file

@ -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<Builder, SuspendDeliveryCommand>() {
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
}
}

View file

@ -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
}

View file

@ -77,6 +77,7 @@ interface OmnipodDashPodStateManager {
var basalProgram: BasalProgram?
val activeCommand: ActiveCommand?
val lastBolus: LastBolus?
var suspendAlertsEnabled: Boolean
fun increaseMessageSequenceNumber()
fun increaseEapAkaSequenceNumber(): ByteArray

View file

@ -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

View file

@ -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,17 +633,9 @@ 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 {
// disable the 'suspendDelivery' button.
buttonBinding.buttonSuspendDelivery.visibility = View.GONE
}
}
private fun updateSetTimeButton() {
if (podStateManager.isActivationCompleted && !podStateManager.sameTimeZone) {
@ -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)

View file

@ -47,4 +47,5 @@
<string name="omnipod_common_history_tbr_value">Rate: %1$.2f U, duration: %2$d minutes</string>
<string name="omnipod_common_history_bolus_value">%1$.2f U</string>
<string name="dash_bolusdelivering">Delivering %1$.2f U</string>
<string name="omnipod_common_alert_delivery_suspended">Insulin delivery is suspended</string>
</resources>

View file

@ -105,11 +105,6 @@
android:title="@string/omnipod_common_preferences_category_other"
app:initialExpandedChildrenCount="0">
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_omnipod_common_suspend_delivery_button_enabled"
android:title="@string/omnipod_common_preferences_suspend_delivery_button_enabled" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_omnipod_common_time_change_event_enabled"

View file

@ -85,7 +85,7 @@ class ProgramAlertsCommandTest {
false,
AlertTrigger.TimerTrigger(4079.toShort()),
BeepType.FOUR_TIMES_BIP_BEEP,
BeepRepetitionType.XXX2
BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN
)
)

View file

@ -0,0 +1,20 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType
import org.apache.commons.codec.DecoderException
import org.apache.commons.codec.binary.Hex
import org.junit.Assert
import org.junit.Test
class SuspendDeliveryCommandTest {
@Test @Throws(DecoderException::class) fun testSuspendDelivery() {
val encoded = SuspendDeliveryCommand.Builder()
.setUniqueId(37879811)
.setSequenceNumber(0.toShort())
.setNonce(1229869870)
.setBeepType(BeepType.LONG_SINGLE_BEEP)
.build()
.encoded
Assert.assertArrayEquals(Hex.decodeHex("0242000300131f05494e532e67190a494e532e680000140302811f"), encoded)
}
}