diff --git a/pump/combov2/src/main/kotlin/info/nightscout/pump/combov2/ComboV2Fragment.kt b/pump/combov2/src/main/kotlin/info/nightscout/pump/combov2/ComboV2Fragment.kt index eac6eeacab..c8d9cd62e8 100644 --- a/pump/combov2/src/main/kotlin/info/nightscout/pump/combov2/ComboV2Fragment.kt +++ b/pump/combov2/src/main/kotlin/info/nightscout/pump/combov2/ComboV2Fragment.kt @@ -41,6 +41,7 @@ class ComboV2Fragment : DaggerFragment() { binding.combov2RefreshButton.setOnClickListener { binding.combov2RefreshButton.isEnabled = false + combov2Plugin.clearPumpErrorObservedFlag() commandQueue.readStatus(rh.gs(info.nightscout.core.ui.R.string.user_request), null) } diff --git a/pump/combov2/src/main/kotlin/info/nightscout/pump/combov2/ComboV2Plugin.kt b/pump/combov2/src/main/kotlin/info/nightscout/pump/combov2/ComboV2Plugin.kt index 6a71eef58a..5a5b3d3afc 100644 --- a/pump/combov2/src/main/kotlin/info/nightscout/pump/combov2/ComboV2Plugin.kt +++ b/pump/combov2/src/main/kotlin/info/nightscout/pump/combov2/ComboV2Plugin.kt @@ -95,6 +95,8 @@ import info.nightscout.comboctl.base.Tbr as ComboCtlTbr import info.nightscout.comboctl.main.Pump as ComboCtlPump import info.nightscout.comboctl.main.PumpManager as ComboCtlPumpManager +internal const val PUMP_ERROR_TIMEOUT_INTERVAL_MSECS = 1000L * 60 * 5 + @Singleton class ComboV2Plugin @Inject constructor ( injector: HasAndroidInjector, @@ -161,6 +163,13 @@ class ComboV2Plugin @Inject constructor ( private var lastConnectionTimestamp = 0L private var lastComboAlert: AlertScreenContent? = null + // States for when the pump reports an error. We then want isInitialized() + // to return false until either the user presses the Refresh button or the + // pumpErrorTimeoutJob expires. That way, the loop won't run until then, + // giving the user a chance to handle the error. + private var pumpErrorObserved = false + private var pumpErrorTimeoutJob: Job? = null + // Set to true if a disconnect request came in while the driver // was in the Connecting, CheckingPump, or ExecutingCommand // state (in other words, while isBusy() was returning true). @@ -183,7 +192,7 @@ class ComboV2Plugin @Inject constructor ( private var activeBasalProfile: BasalProfile? = null // This is used for checking that the correct basal profile is // active in the Combo. If not, loop invocation is disallowed. - // This is _not_ reset by disconect(). That's on purpose; it + // This is _not_ reset by disconnect(). That's on purpose; it // is read by isLoopInvocationAllowed(), which is called even // if the pump is not connected. private var lastActiveBasalProfileNumber: Int? = null @@ -381,7 +390,7 @@ class ComboV2Plugin @Inject constructor ( } override fun isInitialized(): Boolean = - isPaired() && (driverStateFlow.value != DriverState.NotInitialized) + isPaired() && (driverStateFlow.value != DriverState.NotInitialized) && !pumpErrorObserved override fun isSuspended(): Boolean = when (driverStateUIFlow.value) { @@ -432,6 +441,16 @@ class ComboV2Plugin @Inject constructor ( return } + if (pumpErrorObserved) { + aapsLogger.debug(LTag.PUMP, "Aborting connect attempt since the pumpErrorObserved flag is set") + uiInteraction.addNotification( + Notification.COMBO_PUMP_ALARM, + text = rh.gs(R.string.combov2_cannot_connect_pump_error_observed), + level = Notification.NORMAL + ) + return + } + when (driverStateFlow.value) { DriverState.Connecting, DriverState.CheckingPump, @@ -1398,6 +1417,14 @@ class ComboV2Plugin @Inject constructor ( commandQueue.readStatus(reason, null) } + fun clearPumpErrorObservedFlag() { + stopPumpErrorTimeout() + if (pumpErrorObserved) { + aapsLogger.info(LTag.PUMP, "Clearing pumpErrorObserved flag") + pumpErrorObserved = false + } + } + /*** Loop constraints ***/ // These restrict the function of the loop in case of an event // that makes running a loop too risky, for example because something @@ -1550,6 +1577,8 @@ class ComboV2Plugin @Inject constructor ( _serialNumberUIFlow.value = "" _bluetoothAddressUIFlow.value = "" + clearPumpErrorObservedFlag() + // The unpairing variable is set to false in // the PumpManager onPumpUnpaired callback. } @@ -1748,6 +1777,23 @@ class ComboV2Plugin @Inject constructor ( } } + private fun startPumpErrorTimeout() { + if (pumpErrorTimeoutJob != null) + return + + pumpErrorTimeoutJob = pumpCoroutineScope.launch { + delay(PUMP_ERROR_TIMEOUT_INTERVAL_MSECS) + aapsLogger.info(LTag.PUMP, "Clearing pumpErrorObserved flag after timeout was reached") + pumpErrorObserved = false + commandQueue.readStatus(rh.gs(R.string.combov2_refresh_pump_status_after_error), null) + } + } + + private fun stopPumpErrorTimeout() { + pumpErrorTimeoutJob?.cancel() + pumpErrorTimeoutJob = null + } + private fun updateBaseBasalRateUI() { val currentHour = DateTime().hourOfDay().get() // This sets value to null if no profile is set, @@ -2142,6 +2188,12 @@ class ComboV2Plugin @Inject constructor ( } private fun notifyAboutComboAlert(alert: AlertScreenContent) { + if (alert is AlertScreenContent.Error) { + aapsLogger.info(LTag.PUMP, "Error screen observed - setting pumpErrorObserved flag") + pumpErrorObserved = true + startPumpErrorTimeout() + } + uiInteraction.addNotification( Notification.COMBO_PUMP_ALARM, text = "${rh.gs(R.string.combov2_combo_alert)}: ${getAlertDescription(alert)}", diff --git a/pump/combov2/src/main/res/values/strings.xml b/pump/combov2/src/main/res/values/strings.xml index ca0047ff78..73466f753f 100644 --- a/pump/combov2/src/main/res/values/strings.xml +++ b/pump/combov2/src/main/res/values/strings.xml @@ -135,4 +135,6 @@ buttons at the same time to cancel pairing)\n Date and/or time changed Daylight savings time (DST) started Daylight savings time (DST) ended + Cannot connect to pump because the pump reported an error. User must handle the error and then either wait 5 minutes or press the Refresh button in the driver tab. + Refreshing pump status after the pump reported an error