diff --git a/core/data/src/main/kotlin/app/aaps/core/data/pump/defs/DoseStepSize.kt b/core/data/src/main/kotlin/app/aaps/core/data/pump/defs/DoseStepSize.kt index e0078146a64..374a1ff1c91 100644 --- a/core/data/src/main/kotlin/app/aaps/core/data/pump/defs/DoseStepSize.kt +++ b/core/data/src/main/kotlin/app/aaps/core/data/pump/defs/DoseStepSize.kt @@ -32,6 +32,17 @@ enum class DoseStepSize(private val entries: Array) { DoseStepSizeEntry(10.0, Double.MAX_VALUE, 0.1) ) ), + TandemMobiBasal( + arrayOf( + DoseStepSizeEntry(0.0, 0.1, 0.1), + DoseStepSizeEntry(0.1, 15.0, 0.001) + ) + ), + TandemMobiBolus( + arrayOf( + DoseStepSizeEntry(0.05, 25.0, 0.01), + ) + ), YpsopumpBasal( arrayOf( DoseStepSizeEntry(0.0, 1.0, 0.01), diff --git a/core/data/src/main/kotlin/app/aaps/core/data/pump/defs/PumpType.kt b/core/data/src/main/kotlin/app/aaps/core/data/pump/defs/PumpType.kt index 8344f9e0445..fd5a2d13170 100644 --- a/core/data/src/main/kotlin/app/aaps/core/data/pump/defs/PumpType.kt +++ b/core/data/src/main/kotlin/app/aaps/core/data/pump/defs/PumpType.kt @@ -353,14 +353,14 @@ enum class PumpType( manufacturer = ManufacturerType.Tandem, model = "Mobi", bolusSize = 0.01, - specialBolusSize = null, + specialBolusSize = DoseStepSize.TandemMobiBolus, extendedBolusSettings = DoseSettings(0.01, 15, 8 * 60, 0.4), pumpTempBasalType = PumpTempBasalType.Percent, tbrSettings = DoseSettings(5.0, 15, 72 * 60, 0.0, 250.0), specialBasalDurations = arrayOf(Capability.BasalRate_Duration15minAllowed, Capability.BasalRate_Duration30minAllowed), baseBasalMinValue = 0.1, baseBasalStep = 0.001, - baseBasalSpecialSteps = null, + baseBasalSpecialSteps = DoseStepSize.TandemMobiBasal, // reservoirSize = 200, pumpCapability = PumpCapability.TandemMobiCapabilities, source = Source.Tandem diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 86c8ddc6f62..9ea56b9d926 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -194,5 +194,5 @@ androidx-compose-runtime-livedata = { group = "androidx.compose.runtime", name = com-jakewharton-timber = { group = "com.jakewharton.timber", name = "timber", version="5.0.1" } com-github-weliem-blessed-android = { group = "com.github.weliem", name = "blessed-android", version="2.4.0" } io-github-vanpra-compose-dialogs-datetime = { group = "io.github.vanpra.compose-material-dialogs", name = "datetime", version="0.9.0" } -com-github-jwoglom-pumpx2-android = { group = "com.github.jwoglom.pumpX2", name = "pumpx2-android", version="v1.8.0" } +com-github-jwoglom-pumpx2-android = { group = "com.github.jwoglom.pumpX2", name = "pumpx2-android", version="v1.8.2" } diff --git a/pump/common/src/main/kotlin/app/aaps/pump/common/driver/ble/PumpBLESelector.kt b/pump/common/src/main/kotlin/app/aaps/pump/common/driver/ble/PumpBLESelector.kt index 5be3e53cfc6..c6861410c25 100644 --- a/pump/common/src/main/kotlin/app/aaps/pump/common/driver/ble/PumpBLESelector.kt +++ b/pump/common/src/main/kotlin/app/aaps/pump/common/driver/ble/PumpBLESelector.kt @@ -110,6 +110,7 @@ interface PumpBLESelector { enum class PumpBLESelectorText { SCAN_TITLE, + INSTRUCTIONS_TEXT, SELECTED_PUMP_TITLE, REMOVE_TITLE, REMOVE_TEXT, diff --git a/pump/common/src/main/kotlin/app/aaps/pump/common/driver/ui/PumpBLEConfigActivity.kt b/pump/common/src/main/kotlin/app/aaps/pump/common/driver/ui/PumpBLEConfigActivity.kt index 934d424ebef..6e718b2937f 100644 --- a/pump/common/src/main/kotlin/app/aaps/pump/common/driver/ui/PumpBLEConfigActivity.kt +++ b/pump/common/src/main/kotlin/app/aaps/pump/common/driver/ui/PumpBLEConfigActivity.kt @@ -105,6 +105,7 @@ open class PumpBLEConfigActivity : TranslatedDaggerAppCompatActivity() { binding.pumpBleConfigCurrentlySelectedText.text = bleSelector.getText(PumpBLESelectorText.SELECTED_PUMP_TITLE) binding.pumpBleConfigScanTitle.text = bleSelector.getText(PumpBLESelectorText.SCAN_TITLE) + binding.pumpBleConfigInstructionsText.text = bleSelector.getText(PumpBLESelectorText.INSTRUCTIONS_TEXT) title = bleSelector.getText(PumpBLESelectorText.PUMP_CONFIGURATION) supportActionBar?.setDisplayHomeAsUpEnabled(true) diff --git a/pump/common/src/main/res/layout/pump_ble_config_activity.xml b/pump/common/src/main/res/layout/pump_ble_config_activity.xml index b72fbd7e3f7..6f69ba1ab73 100644 --- a/pump/common/src/main/res/layout/pump_ble_config_activity.xml +++ b/pump/common/src/main/res/layout/pump_ble_config_activity.xml @@ -4,6 +4,12 @@ android:orientation="vertical" android:padding="5dp"> + + + android:orientation="horizontal" + android:paddingTop="24dp"> = hashSetOf( - // "17-0x2032" // LOW INSULIN ALERT2 - // ) - // - // private fun isNotFiltered(message: Message): Boolean { - // if (message is MalfunctionStatusResponse) { - // val malfunction : MalfunctionStatusResponse = message as MalfunctionStatusResponse - // return !(malfunctionFilterSet.contains(malfunction.errorString)) - // } else - // return true; - // } - private fun unsuccessfulAlert(req: String) { aapsLogger.error(TAG,"$req was not successful. The pump returned an error fulfilling the request." ) diff --git a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/config/TandemBLESelector.kt b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/config/TandemBLESelector.kt index b41394869da..28491184f26 100644 --- a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/config/TandemBLESelector.kt +++ b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/config/TandemBLESelector.kt @@ -106,7 +106,9 @@ class TandemBLESelector @Inject constructor( aapsLogger.debug(TAG, "TANDEMDBG: onDeviceSelected: ${bleAddress} ") - val selectionOnly = activity.intent?.getBooleanExtra(TandemPumpBLEConfigActivity.EXTRA_SELECTION_ONLY, false) ?: false + val selectionOnly = activity.intent?.getBooleanExtra( + TandemPumpBLEConfigActivity.EXTRA_SELECTION_ONLY, false + ) ?: false //var addressChanged = false @@ -193,11 +195,13 @@ class TandemBLESelector @Inject constructor( when (key) { PumpBLESelectorText.SCAN_TITLE -> stringId = R.string.tandem_ble_config_scan_title + PumpBLESelectorText.INSTRUCTIONS_TEXT -> stringId = R.string.tandem_wizard_waiting_device_selection PumpBLESelectorText.SELECTED_PUMP_TITLE -> stringId = R.string.tandem_ble_config_selected_pump_title PumpBLESelectorText.REMOVE_TITLE -> stringId = R.string.tandem_ble_config_remove_pump_title PumpBLESelectorText.REMOVE_TEXT -> stringId = R.string.tandem_ble_config_remove_pump_confirmation PumpBLESelectorText.NO_SELECTED_PUMP -> stringId = R.string.tandem_ble_config_no_pump_selected PumpBLESelectorText.PUMP_CONFIGURATION -> stringId = R.string.tandem_configuration + else -> return "" } return resourceHelper.gs(stringId) diff --git a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/connector/TandemPumpConnectionManager.kt b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/connector/TandemPumpConnectionManager.kt index a2ac99b7575..9b2110ef8f0 100755 --- a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/connector/TandemPumpConnectionManager.kt +++ b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/connector/TandemPumpConnectionManager.kt @@ -246,7 +246,7 @@ class TandemPumpConnectionManager @Inject constructor( GET_MALFUNCTIONS -> { val malfunctionStatus = responseData.value as MalfunctionStatusDto - if (malfunctionStatus.hasMalfunction()) { + if (malfunctionStatus.hasMalfunction() && !malfunctionStatus.malfunctionMatchesActiveAlertOrAlarm) { setNotificationSemaphore() } } diff --git a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/connector/TandemPumpConnector.kt b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/connector/TandemPumpConnector.kt index 92bb7475d34..c62ce8d8b8d 100644 --- a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/connector/TandemPumpConnector.kt +++ b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/connector/TandemPumpConnector.kt @@ -1,7 +1,6 @@ package app.aaps.pump.tandem.common.driver.connector import android.content.Context -import app.aaps.core.data.model.BS import app.aaps.core.interfaces.logging.AAPSLogger import app.aaps.core.interfaces.logging.LTag import app.aaps.core.interfaces.notifications.Notification @@ -14,7 +13,6 @@ import app.aaps.core.interfaces.rx.events.EventOverviewBolusProgress import app.aaps.core.interfaces.rx.events.EventOverviewBolusStopDeliveryEnabled import app.aaps.core.interfaces.sharedPreferences.SP import app.aaps.core.keys.interfaces.Preferences -import app.aaps.core.ui.extensions.runOnUiThread import app.aaps.pump.common.data.BasalProfileDto import app.aaps.pump.common.data.PumpTimeDifferenceDto import app.aaps.pump.common.defs.BolusData @@ -49,6 +47,7 @@ import app.aaps.pump.tandem.common.driver.connector.response.AlarmStatusDto import app.aaps.pump.tandem.common.driver.connector.response.AlertStatusDto import app.aaps.pump.tandem.common.driver.connector.response.HomeScreenMirrorDto import app.aaps.pump.tandem.common.driver.connector.response.MalfunctionStatusDto +import app.aaps.pump.tandem.common.driver.connector.response.PumpGlobalsDto import app.aaps.pump.tandem.common.driver.connector.response.PumpVersionDto import app.aaps.pump.tandem.common.keys.TandemStringPreferenceKey import app.aaps.pump.tandem.common.util.PumpX2L @@ -57,6 +56,7 @@ import com.jwoglom.pumpx2.pump.PumpState import com.jwoglom.pumpx2.pump.bluetooth.TandemConfig import com.jwoglom.pumpx2.pump.messages.Message import com.jwoglom.pumpx2.pump.messages.models.InsulinUnit +import com.jwoglom.pumpx2.pump.messages.models.NotificationBundle import com.jwoglom.pumpx2.pump.messages.models.PairingCodeType import com.jwoglom.pumpx2.pump.messages.models.StatusMessage import com.jwoglom.pumpx2.pump.messages.request.control.BolusPermissionRequest @@ -72,7 +72,6 @@ import com.jwoglom.pumpx2.pump.messages.request.control.SetMaxBolusLimitRequest import com.jwoglom.pumpx2.pump.messages.request.control.SetQuickBolusSettingsRequest import com.jwoglom.pumpx2.pump.messages.request.control.SetTempRateRequest import com.jwoglom.pumpx2.pump.messages.request.control.StopTempRateRequest -import com.jwoglom.pumpx2.pump.messages.request.control.SuspendPumpingRequest import com.jwoglom.pumpx2.pump.messages.request.currentStatus.AlarmStatusRequest import com.jwoglom.pumpx2.pump.messages.request.currentStatus.AlertStatusRequest import com.jwoglom.pumpx2.pump.messages.request.currentStatus.BasalLimitSettingsRequest @@ -108,7 +107,6 @@ import com.jwoglom.pumpx2.pump.messages.response.control.SetMaxBolusLimitRespons import com.jwoglom.pumpx2.pump.messages.response.control.SetQuickBolusSettingsResponse import com.jwoglom.pumpx2.pump.messages.response.control.SetTempRateResponse import com.jwoglom.pumpx2.pump.messages.response.control.StopTempRateResponse -import com.jwoglom.pumpx2.pump.messages.response.control.SuspendPumpingResponse import com.jwoglom.pumpx2.pump.messages.response.currentStatus.AlarmStatusResponse import com.jwoglom.pumpx2.pump.messages.response.currentStatus.AlertStatusResponse import com.jwoglom.pumpx2.pump.messages.response.currentStatus.BasalIQStatusResponse @@ -167,8 +165,8 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt // TODO Better Error response handling - fun getCommunicationManager(): TandemCommunicationManager { - return tandemCommunicationManager!! + fun getCommunicationManager(): TandemCommunicationManager? { + return tandemCommunicationManager } @@ -222,7 +220,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt this.btAddressUsed = newBtAddress } - return getCommunicationManager().connect() + return getCommunicationManager()!!.connect() } @@ -230,8 +228,13 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt override fun disconnectFromPump(): Boolean { aapsLogger.info(TAG, "disconnectFromPump") - getCommunicationManager().disconnect() - return true + val c = getCommunicationManager() + return if (c == null) { + aapsLogger.warn(TAG, "disconnectFromPump: nothing to do, communication manager already destroyed") + false + } else { + c.disconnect() + } } @@ -253,18 +256,24 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt val map: MutableMap = mutableMapOf() try { - addToSettings(TandemPumpSettingType.CONTROL_IQ_ENABLED, map, getCommunicationManager().sendCommand(getCorrectRequest(TandemCommandType.ControlIQInfo))) - addToSettings(TandemPumpSettingType.BASAL_LIMIT, map, getCommunicationManager().sendCommand(BasalLimitSettingsRequest())) - addToSettings(TandemPumpSettingType.MAX_BOLUS, map, getCommunicationManager().sendCommand(GlobalMaxBolusSettingsRequest())) - addToSettings(TandemPumpSettingType.QUICK_BOLUS, map, getCommunicationManager().sendCommand(PumpGlobalsRequest())) + val comm = getCommunicationManager() + if (comm == null) { + return DataCommandResponse( + PumpCommandType.GetSettings, false, "Problem reading settings, no active communicationManager", map + ) + } + addToSettings(TandemPumpSettingType.CONTROL_IQ_ENABLED, map, comm.sendCommand(getCorrectRequest(TandemCommandType.ControlIQInfo))) + addToSettings(TandemPumpSettingType.BASAL_LIMIT, map, comm.sendCommand(BasalLimitSettingsRequest())) + addToSettings(TandemPumpSettingType.MAX_BOLUS, map, comm.sendCommand(GlobalMaxBolusSettingsRequest())) + addToSettings(TandemPumpSettingType.QUICK_BOLUS, map, comm.sendCommand(PumpGlobalsRequest())) if (this.tandemPumpStatus.tandemPumpFirmware.isSameVersion(TandemPumpApiVersion.VERSION_2_1_to_2_4)) { if (tandemPumpStatus.featuresV1==null) { - addToSettings(TandemPumpSettingType.PUMP_FEATURES_1, map, getCommunicationManager().sendCommand(PumpFeaturesV1Request())) + addToSettings(TandemPumpSettingType.PUMP_FEATURES_1, map, comm.sendCommand(PumpFeaturesV1Request())) } } else { if (tandemPumpStatus.featuresV2==null) { - addToSettings(TandemPumpSettingType.PUMP_FEATURES_2, map, getCommunicationManager().sendCommand(PumpFeaturesV2Request())) + addToSettings(TandemPumpSettingType.PUMP_FEATURES_2, map, comm.sendCommand(PumpFeaturesV2Request())) } } @@ -378,11 +387,11 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt // isSMB = detailedBolusInfo.bolusType === BS.Type.SMB, // id = detailedBolusInfo.id) - sendBolusEvent(bolusEvent = TandemBolusEvent.Preparing, + sendBolusEvent(bolusEvent = TandemBolusEvent.Initiating, fullAmount = detailedBolusInfo.insulin, id = detailedBolusInfo.id) - val permissionResponseMessage: BolusPermissionResponse? = getCommunicationManager().sendCommand( + val permissionResponseMessage: BolusPermissionResponse? = getCommunicationManager()?.sendCommand( BolusPermissionRequest() ) as BolusPermissionResponse? @@ -418,7 +427,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt 0, 0, bolusCarbs, 0, 0) - val bolusRequestResponse: InitiateBolusResponse? = getCommunicationManager().sendCommand( + val bolusRequestResponse: InitiateBolusResponse? = getCommunicationManager()?.sendCommand( bolusRequest ) as InitiateBolusResponse? @@ -434,7 +443,12 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt aapsLogger.debug(TAG, "BolusRequest: $bolusRequest and response: $bolusRequestResponse") - if (!bolusRequestResponse.isStatusOK) { + sendBolusEvent(bolusEvent = TandemBolusEvent.Preparing, + fullAmount = detailedBolusInfo.insulin, + id = detailedBolusInfo.id) + + + if (!bolusRequestResponse.wasBolusInitiated()) { aapsLogger.error(TAG, "InitiateBolusResponse status was not ok, bolus was not started: status: ${bolusRequestResponse.statusType.name}." ) return DataCommandResponse( @@ -446,43 +460,69 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt var finished = false var bolusStatusResponse: CurrentBolusStatusResponse? = null - var deliveryStartTime: Long = System.currentTimeMillis() - var startedSending = false + var initiateStartTime: Long = System.currentTimeMillis() + var deliveryStartTime: Long? = null + var startedRequesting = false + var startedDelivering = false - // TODO(jwoglom): ?? val maxBolus = detailedBolusInfo.insulin - 0.01 while (!finished) { - Thread.sleep(1000) + Thread.sleep(500) bolusStatusResponse = getCommunicationManager() - .sendCommand(CurrentBolusStatusRequest()) as CurrentBolusStatusResponse? + ?.sendCommand(CurrentBolusStatusRequest()) as CurrentBolusStatusResponse? if (bolusStatusResponse==null) { aapsLogger.warn(TAG, "No response for CurrentBolusStatusResponse") + val elapsedSeconds = (System.currentTimeMillis() - initiateStartTime) / 1000 + if (elapsedSeconds >= 30) { + return DataCommandResponse( + PumpCommandType.SetBolus, false, + "Never received BolusStatusResponse", + null + ) + } } else { if (bolusStatusResponse.status== CurrentBolusStatusResponse.CurrentBolusStatus.ALREADY_DELIVERED_OR_INVALID) { - aapsLogger.error(TAG, "Bolus delivered: " + getJsonStringFromObject(bolusStatusResponse)) - finished = true - val bolusTimeSec = ((System.currentTimeMillis() - deliveryStartTime!!)/1000).toInt() - aapsLogger.error(TAG, "Bolus: amount=${detailedBolusInfo.insulin}, bolusId=$bolusId, timeSeconds=${bolusTimeSec}") - bolusId = 0 - sendBolusEvent(bolusEvent = TandemBolusEvent.DeliveryDone) + if (startedDelivering) { + aapsLogger.error(TAG, "Bolus delivered: " + getJsonStringFromObject(bolusStatusResponse)) + finished = true + val bolusTimeSec = ((System.currentTimeMillis() - initiateStartTime) / 1000).toInt() + aapsLogger.error(TAG, "Bolus: amount=${detailedBolusInfo.insulin}, bolusId=$bolusId, timeSeconds=${bolusTimeSec}") + bolusId = 0 + sendBolusEvent(bolusEvent = TandemBolusEvent.DeliveryDone) + } else { + val elapsedSeconds = (System.currentTimeMillis() - initiateStartTime) / 1000 + if (elapsedSeconds >= 30) { + return DataCommandResponse( + PumpCommandType.SetBolus, false, + "Never received BolusStatusResponse", + null + ) + } + } } else { //aapsLogger.error(TAG, "Bolus status: ${bolusStatusResponse.status.name}") - if (bolusStatusResponse.status == CurrentBolusStatusResponse.CurrentBolusStatus.DELIVERING) { + if (bolusStatusResponse.status == CurrentBolusStatusResponse.CurrentBolusStatus.REQUESTING) { + if (!startedRequesting) { + startedRequesting = true + sendBolusEvent(bolusEvent = TandemBolusEvent.Requesting) + } - if (!startedSending) { + } else if (bolusStatusResponse.status == CurrentBolusStatusResponse.CurrentBolusStatus.DELIVERING) { + + if (!startedDelivering) { rxBus.send(EventOverviewBolusStopDeliveryEnabled(isEnabled = true)) - startedSending = true + startedDelivering = true } - if (deliveryStartTime==null) { - deliveryStartTime = System.currentTimeMillis() + if (initiateStartTime==null) { + initiateStartTime = System.currentTimeMillis() aapsLogger.error(TAG, "Bolus status: ${bolusStatusResponse.status.name}") } else { - val bolusTimeSec = ((System.currentTimeMillis() - deliveryStartTime)/1000).toInt() + val bolusTimeSec = ((System.currentTimeMillis() - initiateStartTime)/1000).toInt() val bolusAmount = 0.035714286 * bolusTimeSec if (bolusAmount > maxBolus) { @@ -495,15 +535,15 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt } } else { aapsLogger.error(TAG, "Bolus status: ${bolusStatusResponse.status.name}") - deliveryStartTime = System.currentTimeMillis() + initiateStartTime = System.currentTimeMillis() } } } } - // it seems that when CurrentBolusStatusResponse comes back as delivered, we don't have - // any bolus details, so we just read bolus again... + // when CurrentBolusStatusResponse comes back as delivered, we don't have + // any bolus details, so we just read the latest bolus which just occurred val bolusDataCommandResponse = getBolus() if (bolusDataCommandResponse.isSuccess) { @@ -525,16 +565,24 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt var status = "" when(bolusEvent) { + TandemBolusEvent.Initiating -> { + percent = 0 + status = resourceHelper.gs(Rpc.string.bolus_preparing, fullAmount) + } TandemBolusEvent.Preparing -> { - percent = 1 + percent = 5 status = resourceHelper.gs(Rpc.string.bolus_preparing,fullAmount) } + TandemBolusEvent.Requesting -> { + percent = 10 + status = resourceHelper.gs(Rpc.string.bolus_preparing, fullAmount) + } TandemBolusEvent.Delivering -> { - percent = ((amountBolus/fullAmount)*100).toInt() - if (percent>=100) { - percent = 99 + percent = 10 + ((amountBolus/fullAmount)*90).toInt() + if (percent>=95) { + percent = 95 } else if (percent<1) { - percent = 1 + percent = 0 } var amountBolus2 = amountBolus @@ -551,7 +599,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt } } - aapsLogger.error(TAG, "Sending Bolus Event: status=${status}, progress=${percent}") + aapsLogger.info(TAG, "Sending Bolus Event: status=${status}, progress=${percent}") rxBus.send(EventOverviewBolusProgress(status = status, percent = percent, id = id)) @@ -559,8 +607,29 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt enum class TandemBolusEvent { + /** + * Initiating: calling BolusPermissionRequest and InitiateBolusRequest commands + */ + Initiating, + + /** + * Preparing: between call to InitiateBolusRequest and when we receive data on CurrentBolusStatusRequest + */ Preparing, + + /** + * Requesting: CurrentBolusStatusResponse.CurrentBolusStatus.REQUESTING (pump preparing piston) + */ + Requesting, + + /** + * Delivering: CurrentBolusStatusResponse.CurrentBolusStatus.DELIVERING (pump delivering insulin) + */ Delivering, + + /** + * DeliveryDone: insulin delivered and pump bolus operation complete + */ DeliveryDone } @@ -575,24 +644,46 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt // TODO(jwoglom): since this is a safety critical path, we need to handle bolusId being null // and fall back on requesting active bolus ID and then canceling it. and also do the same // if we get a rejection (status!=0) on CancelBolusResponse. - aapsLogger.info(TAG, "cancelBolus: no BolusId found, exiting") - return DataCommandResponse( - PumpCommandType.CancelBolus, false, "No Bolus Id found, bouls might not be running anymore.", - null - ) + aapsLogger.warn(TAG, "cancelBolus: no BolusId found, fetching the active bolus ID as fallback") + + val currentBolus: CurrentBolusStatusResponse? = getCommunicationManager()?.sendCommand(CurrentBolusStatusRequest()) as CurrentBolusStatusResponse? + aapsLogger.warn(TAG, "cancelBolus: current bolus: $currentBolus") + + if (currentBolus == null || currentBolus.status == CurrentBolusStatusResponse.CurrentBolusStatus.ALREADY_DELIVERED_OR_INVALID) { + + return DataCommandResponse( + PumpCommandType.CancelBolus, false, "No Bolus Id found, bolus either already finished or did not occur", + null + ) + } + + bolusId = currentBolus.bolusId } else { aapsLogger.info(TAG, "cancelBolus (bolusId=$bolusId)") } - val responseMessage: Message? = getCommunicationManager().sendCommand(CancelBolusRequest(bolusId)) - as CancelBolusResponse + val responseMessage: CancelBolusResponse? = getCommunicationManager()?.sendCommand(CancelBolusRequest(bolusId)) + as CancelBolusResponse? - aapsLogger.debug(TAG, "ResponseMessgae: $responseMessage ") + aapsLogger.debug(TAG, "cancelBolus responseMessage: $responseMessage ") - return DataCommandResponse( - PumpCommandType.CancelBolus, true, null, - null - ) + + return if (responseMessage != null && responseMessage.status == CancelBolusResponse.CancelStatus.SUCCESS) { + DataCommandResponse( + PumpCommandType.CancelBolus, + true, + null, + null + ) + } else { + DataCommandResponse( + PumpCommandType.CancelBolus, + false, + "Error cancelling bolus, got status ${responseMessage?.statusId}: $responseMessage", + null + ) + + } } @@ -635,7 +726,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt return super.sendTemporaryBasal(value, duration) } - val responseMessage: SetTempRateResponse? = getCommunicationManager().sendCommand( + val responseMessage: SetTempRateResponse? = getCommunicationManager()?.sendCommand( SetTempRateRequest(duration, value)) as SetTempRateResponse? if (responseMessage!=null) { @@ -674,7 +765,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt } val responseMessage: StopTempRateResponse? = getCommunicationManager() - .sendCommand(StopTempRateRequest()) as StopTempRateResponse? + ?.sendCommand(StopTempRateRequest()) as StopTempRateResponse? return if (responseMessage!=null) { DataCommandResponse( @@ -697,7 +788,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt operation: String): DataCommandResponse { val responseMessage: StatusMessage? = getCommunicationManager() - .sendCommand(requestMessage) as StatusMessage? + ?.sendCommand(requestMessage) as StatusMessage? return if (responseMessage!=null) { DataCommandResponse( @@ -751,7 +842,8 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt aapsLogger.debug(LTag.PUMPCOMM, "IDPSegmentRequest [idpId=$idpId, segmentIndex=$i]") - responseMessage = getCommunicationManager().sendCommand(IDPSegmentRequest(idpId, i)) + responseMessage = getCommunicationManager() + ?.sendCommand(IDPSegmentRequest(idpId, i)) responseText = checkResponse(responseMessage, "IDPSegmentRequest [idpId=${idpId}, segmentNumber=${i}]") @@ -795,7 +887,8 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt fun getInitialBasalProfileConfiguration(): PumpProfileDto { - var responseMessage: Message? = getCommunicationManager().sendCommand(ProfileStatusRequest()) + var responseMessage: Message? = getCommunicationManager() + ?.sendCommand(ProfileStatusRequest()) var responseText = checkResponse(responseMessage, "ProfileStatusRequest") @@ -821,7 +914,8 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt return pumpProfileDto } - responseMessage = getCommunicationManager().sendCommand(IDPSettingsRequest(idpId)) + responseMessage = getCommunicationManager() + ?.sendCommand(IDPSettingsRequest(idpId)) responseText = checkResponse(responseMessage, "IDPSettingsRequest (idpId=${idpId}") @@ -893,11 +987,11 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt profile.dia.toInt() * 60, 0) // for AAPS profile we are setting this to 0 - val responseMessage = getCommunicationManager().sendCommand(createIDPRequest) as CreateIDPResponse + val responseMessage = getCommunicationManager()?.sendCommand(createIDPRequest) as CreateIDPResponse? responseText = checkResponse(responseMessage, "sendBasalProfile - NEW Scenario - Create Initial IDP") - if (responseText!=null) { + if (responseText!=null || responseMessage == null) { aapsLogger.error(LTag.PUMPCOMM, "sendBasalProfile: ERROR: $responseText - NEW Scenario - Create Initial IDP") return DataCommandResponse(PumpCommandType.SetBasalProfile, false, responseText, false) @@ -948,11 +1042,11 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt idpSegments[index].profileISF, idpStatusId) - val responseMessage = getCommunicationManager().sendCommand(setSegment) as SetIDPSegmentResponse + val responseMessage = getCommunicationManager()?.sendCommand(setSegment) as SetIDPSegmentResponse? responseText = checkResponse(responseMessage, "sendBasalProfile - SetIDPSegmentRequest (idpId=${idpId},segmentIndex=$index)") - if (responseText!=null) { + if (responseText!=null || responseMessage == null) { aapsLogger.error(LTag.PUMPCOMM, "sendBasalProfile: ERROR: $responseText SetIDPSegmentRequest [index=${index},operation=$operation]") return DataCommandResponse(PumpCommandType.SetBasalProfile, false, responseText, false) @@ -1029,11 +1123,11 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt idpSegments[index].profileISF, idpStatusId) - val responseMessage = getCommunicationManager().sendCommand(setSegment) as SetIDPSegmentResponse + val responseMessage = getCommunicationManager()?.sendCommand(setSegment) as SetIDPSegmentResponse responseText = checkResponse(responseMessage, "sendBasalProfile - SetIDPSegmentRequest (idpId=${idpId},segmentIndex=$index)") - if (responseText!=null) { + if (responseText!=null || responseMessage == null) { aapsLogger.error(LTag.PUMPCOMM, "sendBasalProfile: ERROR: $responseText SetIDPSegmentRequest [index=${index},operation=$operation]") return DataCommandResponse(PumpCommandType.SetBasalProfile, false, responseText, false) @@ -1056,7 +1150,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt idpSegment.profileCarbRatio, idpSegment.profileTargetBG, idpSegment.profileISF, idpStatusId) - val responseMessage = getCommunicationManager().sendCommand(setSegment) as SetIDPSegmentResponse + val responseMessage = getCommunicationManager()?.sendCommand(setSegment) as SetIDPSegmentResponse? val responseText = checkResponse(responseMessage, "SetIDPSegmentRequest [idpId=${idpSegment.idpId},segmentIndex=$index]") @@ -1065,7 +1159,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt return false } - val success = responseMessage.isStatusOK + val success = responseMessage?.isStatusOK == true aapsLogger.info(LTag.PUMPCOMM, "sendBasalProfile - SetIDPSegmentRequest[segmentIndex=$index,op=DELETE_SEGMENT_ID] Status: success=$success") @@ -1077,7 +1171,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt // TimeSinceResetRequest aapsLogger.info(LTag.PUMPCOMM, "getTime") - val responseMessage = getCommunicationManager().sendCommand(TimeSinceResetRequest()) + val responseMessage = getCommunicationManager()?.sendCommand(TimeSinceResetRequest()) as TimeSinceResetResponse val responseText = checkResponse(responseMessage, "TimeSinceResetRequest") @@ -1105,11 +1199,12 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt return super.setTime() } - val responseMessage = getCommunicationManager().sendCommand(ChangeTimeDateRequest(Instant.now())) as StatusMessage + val responseMessage = getCommunicationManager()?.sendCommand(ChangeTimeDateRequest(Instant.now())) as StatusMessage? + val responseText = checkResponse(responseMessage, "ChangeTimeDateRequest") - if (responseText!=null) { + if (responseText!=null || responseMessage == null) { return DataCommandResponse(PumpCommandType.SetTime, false, responseText, false) } @@ -1131,7 +1226,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt aapsLogger.info(LTag.PUMPCOMM, "getPumpStatus") val responseMessage: HomeScreenMirrorResponse? = getCommunicationManager() - .sendCommand(HomeScreenMirrorRequest()) as HomeScreenMirrorResponse? + ?.sendCommand(HomeScreenMirrorRequest()) as HomeScreenMirrorResponse? if (responseMessage!=null) { @@ -1202,7 +1297,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt aapsLogger.info(LTag.PUMPCOMM, "dismissNotification $details") val responseMessage: DismissNotificationResponse? = getCommunicationManager() - .sendCommand(dismissNotificationRequest) as DismissNotificationResponse? + ?.sendCommand(dismissNotificationRequest) as DismissNotificationResponse? if (responseMessage!=null) { return DataCommandResponse( @@ -1226,7 +1321,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt aapsLogger.info(TAG, "getPumpInfo") val responseMessage: PumpVersionResponse? = getCommunicationManager() - .sendCommand(PumpVersionRequest()) as PumpVersionResponse? + ?.sendCommand(PumpVersionRequest()) as PumpVersionResponse? if (responseMessage!=null) { @@ -1253,7 +1348,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt aapsLogger.info(LTag.PUMPCOMM, "setMaxBolus [bolusAmount=$bolusAmount]") val responseMessage: SetMaxBolusLimitResponse? = getCommunicationManager() - .sendCommand(SetMaxBolusLimitRequest( + ?.sendCommand(SetMaxBolusLimitRequest( InsulinUnit.from1To1000(bolusAmount.toDouble()).toInt() )) as SetMaxBolusLimitResponse? @@ -1278,7 +1373,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt aapsLogger.info(LTag.PUMPCOMM, "getAlerts") val responseMessage: AlertStatusResponse? = getCommunicationManager() - .sendCommand(AlertStatusRequest()) as AlertStatusResponse? + ?.sendCommand(AlertStatusRequest()) as AlertStatusResponse? if (responseMessage!=null) { @@ -1305,7 +1400,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt aapsLogger.info(LTag.PUMPCOMM, "getAlerts") val responseMessage: AlarmStatusResponse? = getCommunicationManager() - .sendCommand(AlarmStatusRequest()) as AlarmStatusResponse? + ?.sendCommand(AlarmStatusRequest()) as AlarmStatusResponse? if (responseMessage!=null) { @@ -1328,17 +1423,40 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt private fun getMalfunctions(): DataCommandResponse { - + // NOTE: MalfunctionStatusResponse returns data which is not always necessarily + // reflective of an actual malfunction. If the codeA matches an active alarm or alert, + // we ignore the malfunction in favor of notifying for that specific alert code. + // As a result, we will actually re-fetch the current alarm and alert state here + // to then use pumpx2's NotificationBundle filtering logic to prevent errant notifications. aapsLogger.info(LTag.PUMPCOMM, "getMalfunctions") val responseMessage: MalfunctionStatusResponse? = getCommunicationManager() - .sendCommand(MalfunctionStatusRequest()) as MalfunctionStatusResponse? + ?.sendCommand(MalfunctionStatusRequest()) as MalfunctionStatusResponse? if (responseMessage!=null) { - val malfunctionStatusDto = MalfunctionStatusDto() + val bundle = NotificationBundle() + val alertResponse: AlertStatusResponse? = getCommunicationManager() + ?.sendCommand(AlertStatusRequest()) as AlertStatusResponse? + if (alertResponse != null) { + bundle.add(alertResponse) + } + + val alarmResponse: AlarmStatusResponse? = getCommunicationManager() + ?.sendCommand(AlarmStatusRequest()) as AlarmStatusResponse? + if (alarmResponse != null) { + bundle.add(alarmResponse) + } + + // if after adding the malfunction response to the bundle + // it is automatically excluded, then ignore it + bundle.add(responseMessage) + val malfunctionMatchesActiveAlertOrAlarm = bundle.get().none { it is MalfunctionStatusResponse } + + val malfunctionStatusDto = MalfunctionStatusDto(malfunctionMatchesActiveAlertOrAlarm) malfunctionStatusDto.parse(responseMessage.cargo) + return DataCommandResponse( PumpCommandType.CustomCommand, true, null, @@ -1356,32 +1474,23 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt private fun getQuickBolus(): DataCommandResponse { - aapsLogger.error(LTag.PUMPCOMM, "getQuickBolus Not implemented") - - // TODO: IMPLEMENT - return DataCommandResponse( - PumpCommandType.CustomCommand, false, - "getQuickBolus Not implemented", - null) - - - val responseMessage: AlertStatusResponse? = getCommunicationManager() - .sendCommand((AlertStatusRequest())) as AlertStatusResponse? + val responseMessage: PumpGlobalsResponse? = getCommunicationManager() + ?.sendCommand((PumpGlobalsRequest())) as PumpGlobalsResponse? if (responseMessage!=null) { - val alertStatusDto = AlertStatusDto() - alertStatusDto.parse(responseMessage.cargo) + val pumpGlobalsDto = PumpGlobalsDto() + pumpGlobalsDto.parse(responseMessage.cargo) return DataCommandResponse( PumpCommandType.CustomCommand, true, null, - alertStatusDto + pumpGlobalsDto ) } else { return DataCommandResponse( PumpCommandType.CustomCommand, false, - "Error getting response from sending AlertStatusRequest: null", + "Error getting response for getQuickBolus: null", null ) } @@ -1389,12 +1498,18 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt + /** + * @param basalAmount units + */ private fun setMaxBasal(basalAmount: Int): DataCommandResponse { aapsLogger.info(LTag.PUMPCOMM, "setMaxBasal [basalAmount=$basalAmount]") + // units -> milliunits + val newMaxBasalLimit = SetMaxBasalLimitRequest(basalAmount*1000) + val responseMessage: SetMaxBasalLimitResponse? = getCommunicationManager() - .sendCommand(SetMaxBasalLimitRequest(basalAmount*1000)) as SetMaxBasalLimitResponse? + ?.sendCommand(newMaxBasalLimit) as SetMaxBasalLimitResponse? if (responseMessage!=null) { return DataCommandResponse( @@ -1418,7 +1533,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt val quickBolusIncrement = SetQuickBolusSettingsRequest.QuickBolusIncrement.valueOf(quickBolusType.name) val responseMessage: SetQuickBolusSettingsResponse? = getCommunicationManager() - .sendCommand(SetQuickBolusSettingsRequest(quickBolusIncrement)) as SetQuickBolusSettingsResponse? + ?.sendCommand(SetQuickBolusSettingsRequest(quickBolusIncrement)) as SetQuickBolusSettingsResponse? if (responseMessage!=null) { return DataCommandResponse( @@ -1442,7 +1557,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt aapsLogger.info(LTag.PUMPCOMM, "setControlIQDisabled") val responseMessage: ChangeControlIQSettingsResponse? = getCommunicationManager() - .sendCommand(ChangeControlIQSettingsRequest(false, 0, 0)) as ChangeControlIQSettingsResponse? + ?.sendCommand(ChangeControlIQSettingsRequest(false, 0, 0)) as ChangeControlIQSettingsResponse? if (responseMessage!=null) { return DataCommandResponse( @@ -1469,7 +1584,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt aapsLogger.info(TAG, "TANDEMDBG: sendAndReceivePumpData for $commandType - Request message: ${requestMessage.javaClass.name}") - val responseMessage: Message? = getCommunicationManager().sendCommand(requestMessage) + val responseMessage: Message? = getCommunicationManager()?.sendCommand(requestMessage) aapsLogger.info(TAG, "TANDEMDBG: sendAndReceivePumpData: Response: $responseMessage") @@ -1543,7 +1658,7 @@ class TandemPumpConnector @Inject constructor(var tandemPumpStatus: TandemPumpSt return if (tandemCommunicationManager==null) { false } else { - getCommunicationManager().connected + getCommunicationManager()?.connected == true } } diff --git a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/connector/response/MalfunctionStatusDto.kt b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/connector/response/MalfunctionStatusDto.kt index 3401cb1fc65..a8fd093d015 100644 --- a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/connector/response/MalfunctionStatusDto.kt +++ b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/connector/response/MalfunctionStatusDto.kt @@ -10,6 +10,7 @@ import com.jwoglom.pumpx2.pump.messages.response.currentStatus.AlarmStatusRespon import com.jwoglom.pumpx2.pump.messages.response.currentStatus.MalfunctionStatusResponse @MessageProps(opCode = 121, size = 11, type = MessageType.RESPONSE, characteristic = Characteristic.CURRENT_STATUS, request = MalfunctionStatusRequest::class) -class MalfunctionStatusDto: MalfunctionStatusResponse(), AdditionalResponseDataInterface { - +class MalfunctionStatusDto( + val malfunctionMatchesActiveAlertOrAlarm: Boolean = false +): MalfunctionStatusResponse(), AdditionalResponseDataInterface { } diff --git a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/connector/response/PumpGlobalsDto.kt b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/connector/response/PumpGlobalsDto.kt new file mode 100644 index 00000000000..8649bfe14d3 --- /dev/null +++ b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/driver/connector/response/PumpGlobalsDto.kt @@ -0,0 +1,17 @@ +package app.aaps.pump.tandem.common.driver.connector.response + +import app.aaps.pump.common.driver.connector.commands.data.AdditionalResponseDataInterface +import com.jwoglom.pumpx2.pump.messages.MessageType +import com.jwoglom.pumpx2.pump.messages.annotations.MessageProps +import com.jwoglom.pumpx2.pump.messages.request.currentStatus.AlarmStatusRequest +import com.jwoglom.pumpx2.pump.messages.request.currentStatus.PumpGlobalsRequest +import com.jwoglom.pumpx2.pump.messages.response.currentStatus.AlertStatusResponse +import com.jwoglom.pumpx2.pump.messages.response.currentStatus.PumpGlobalsResponse + +@MessageProps(opCode = 87, size = 14, type = MessageType.RESPONSE, request = PumpGlobalsRequest::class) +class PumpGlobalsDto: PumpGlobalsResponse(), AdditionalResponseDataInterface { + + + + +} \ No newline at end of file diff --git a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/events/EventTandemPairingStatus.kt b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/events/EventTandemPairingStatus.kt index df01ae15e57..0865cecae2f 100644 --- a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/events/EventTandemPairingStatus.kt +++ b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/events/EventTandemPairingStatus.kt @@ -1,6 +1,8 @@ package app.aaps.pump.tandem.common.events import app.aaps.core.interfaces.rx.events.Event +import app.aaps.pump.tandem.common.data.defs.TandemPumpApiVersion +import com.jwoglom.pumpx2.pump.messages.response.currentStatus.ApiVersionResponse /** * Event for tracking Tandem pump pairing status through the wizard flow @@ -8,8 +10,8 @@ import app.aaps.core.interfaces.rx.events.Event sealed class EventTandemPairingStatus : Event() { object PairingStarted : EventTandemPairingStatus() object WaitingForCode : EventTandemPairingStatus() - object Connecting : EventTandemPairingStatus() - data class PairingSuccess(val pumpSerial: String, val pumpName: String) : EventTandemPairingStatus() + data class PairingInProgress(val progressPercent: Int, val progressLabel: String = "") : EventTandemPairingStatus() + data class PairingSuccess(val pumpSerial: String, val pumpName: String, val pumpApiVersion: TandemPumpApiVersion) : EventTandemPairingStatus() data class PairingFailed(val error: PairingError) : EventTandemPairingStatus() } diff --git a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/util/TandemPumpUtil.kt b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/util/TandemPumpUtil.kt index c89942254ce..4d4cff1682b 100755 --- a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/util/TandemPumpUtil.kt +++ b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/common/util/TandemPumpUtil.kt @@ -110,27 +110,6 @@ class TandemPumpUtil @Inject constructor( } - val malfunctionFilterSet: HashSet = hashSetOf( - "17-0x2032", // LOW INSULIN ALERT2 - "0-0x2032", // LOW INSULIN ALERT - "2-0x2082", // LOW_POWER_ALERTS - ) - - fun isNotificationNotFiltered(message: Message): Boolean { - if (message is MalfunctionStatusResponse) { - return isMalfunctionNotFiltered(message) - } else - return true; - } - - fun isMalfunctionNotFiltered(malfunction: MalfunctionStatusResponse): Boolean { - return !(malfunctionFilterSet.contains(malfunction.errorString)) && !malfunction.hasMalfunction() - } - - - - - companion object { const val MAX_RETRY = 2 diff --git a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/TandemMobiPumpPlugin.kt b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/TandemMobiPumpPlugin.kt index 056c27afabf..90d1db580ac 100755 --- a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/TandemMobiPumpPlugin.kt +++ b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/TandemMobiPumpPlugin.kt @@ -385,6 +385,7 @@ class TandemMobiPumpPlugin @Inject constructor( override fun applyBasalConstraints(absoluteRate: Constraint, profile: Profile): Constraint { + // 1. Check if above 250% of profile rate val maxBasalBySettings = tandemPumpUtil.getIntPreferenceOrDefault(TandemIntPreferenceKey.MaxBasal) val allowedAmount = baseBasalRate * 2.5 // 250% is max allowed @@ -403,6 +404,16 @@ class TandemMobiPumpPlugin @Inject constructor( } } + // 2. Check if below minimum allowed basal rate + // Effective basal rate can be 0.0u/hr, 0.1u/hr, or greater, but not between 0.0-0.1u + // (e.g., 0.05u/hr is not allowed as an effective basal rate) + if (absoluteRate.value() > 0 && absoluteRate.value() < 0.1) { + absoluteRate.set(0.0, + rh.gs(R.string.tandem_constraint_basal_rate_min_0_1u, absoluteRate.value()), + this) + + } + return absoluteRate } @@ -443,6 +454,40 @@ class TandemMobiPumpPlugin @Inject constructor( return percentRate } + override fun applyBolusConstraints(insulin: Constraint): Constraint { + // pump-enforced minimum bolus + val MIN_BOLUS = 0.05 + + // Check minimum bolus constraint + if (insulin.value() > 0.0 && insulin.value() < MIN_BOLUS) { + insulin.set( + 0.0, + rh.gs(R.string.tandem_constraint_bolus_minimum, MIN_BOLUS), + this + ) + } + + // maximum value of max bolus limit + val MAX_BOLUS = 25.0 + val maxBolusBySettings = tandemPumpUtil.getIntPreferenceOrDefault(TandemIntPreferenceKey.MaxBolus) + + + // Check maximum bolus setting + insulin.setIfSmaller( + maxBolusBySettings.toDouble(), + rh.gs(R.string.tandem_constraint_bolus_maximum, maxBolusBySettings), + this + ) + + insulin.setIfSmaller( + MAX_BOLUS, + rh.gs(R.string.tandem_constraint_bolus_maximum, maxBolusBySettings), + this + ) + + return insulin + + } override var serviceConnection: ServiceConnection? = object : ServiceConnection { override fun onServiceDisconnected(name: ComponentName) { @@ -620,7 +665,7 @@ class TandemMobiPumpPlugin @Inject constructor( override fun canHandleDST(): Boolean { - // TODO(jwoglom): pretty sure it can't (requires manual time update) + // TODO(AndyRozman): check DST transitions return true } @@ -921,7 +966,7 @@ class TandemMobiPumpPlugin @Inject constructor( } } is MalfunctionStatusDto -> { - if (valueOfResponse.hasMalfunction()) { + if (valueOfResponse.hasMalfunction() && !valueOfResponse.malfunctionMatchesActiveAlertOrAlarm) { notificationFound = true break; } diff --git a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/actions/PumpInfo.kt b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/actions/PumpInfo.kt index 35b5d68812f..eb6d1defe71 100644 --- a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/actions/PumpInfo.kt +++ b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/actions/PumpInfo.kt @@ -35,8 +35,8 @@ fun PumpInfo(innerPadding: PaddingValues = PaddingValues(), resourceHelper: ResourceHelper ) { - val pumpInfo = LocalTandemDataStore.current.pumpVersionResponse.value!! - val apiVersion = LocalTandemDataStore.current.apiVersionResponse.value!! + val pumpInfo = LocalTandemDataStore.current.pumpVersionResponse.value + val apiVersion = LocalTandemDataStore.current.apiVersionResponse.value val isMobi = if (tandemPumpStatus==null) true else tandemPumpStatus.tandemPumpFirmware.isMobi() @@ -55,35 +55,35 @@ fun PumpInfo(innerPadding: PaddingValues = PaddingValues(), } item { - PumpInfoRow(label= resourceHelper.gs(R.string.pump_serial_number), "${pumpInfo.serialNum}") + PumpInfoRow(label= resourceHelper.gs(R.string.pump_serial_number), "${pumpInfo?.serialNum}") } item { - PumpInfoRow(label= resourceHelper.gs(R.string.pi_pump_sw), value="${apiVersion.majorVersion}.${apiVersion.minorVersion}" /*pumpInfo.pumpRev*/) + PumpInfoRow(label= resourceHelper.gs(R.string.pi_pump_sw), value="${apiVersion?.majorVersion}.${apiVersion?.minorVersion}" /*pumpInfo.pumpRev*/) } item { - PumpInfoRow(label="ARM S/W " + resourceHelper.gs(R.string.pi_version), value="${pumpInfo.armSwVer}") + PumpInfoRow(label="ARM S/W " + resourceHelper.gs(R.string.pi_version), value="${pumpInfo?.armSwVer}") } item { - PumpInfoRow(label=resourceHelper.gs(R.string.pi_sw_part_num), value="${pumpInfo.partNum}") + PumpInfoRow(label=resourceHelper.gs(R.string.pi_sw_part_num), value="${pumpInfo?.partNum}") } item { - PumpInfoRow("ConfigA Bits", value="0x%08X".format(pumpInfo.configABits)) + PumpInfoRow("ConfigA Bits", value="0x%08X".format(pumpInfo?.configABits)) } item { - PumpInfoRow("ConfigB Bits", value="0x%08X".format(pumpInfo.configBBits)) + PumpInfoRow("ConfigB Bits", value="0x%08X".format(pumpInfo?.configBBits)) } item { - PumpInfoRow(label= resourceHelper.gs(R.string.pi_pump_model), value=if (isMobi) "t:Mobi (${pumpInfo.modelNum})" else "t:Slim X2 (${pumpInfo.modelNum})") + PumpInfoRow(label= resourceHelper.gs(R.string.pi_pump_model), value=if (isMobi) "t:Mobi (${pumpInfo?.modelNum})" else "t:Slim X2 (${pumpInfo?.modelNum})") } item { - PumpInfoRow(label= resourceHelper.gs(R.string.pi_pcba_serial), value="${pumpInfo.pcbaSN}") + PumpInfoRow(label= resourceHelper.gs(R.string.pi_pcba_serial), value="${pumpInfo?.pcbaSN}") } @@ -121,7 +121,6 @@ private fun DefaultPreview_PumpInfo() { ) { setUpPreviewState(LocalTandemDataStore.current) var pumpVersion = PumpVersionResponse(3628697757L, 0L, 0L, 0L, 1226976L, 1013045L, "0", 232700077L, "0", 1004000L) - System.out.println(pumpVersion.cargo[24]) LocalTandemDataStore.current.pumpVersionResponse.value = pumpVersion PumpInfo( innerPadding = PaddingValues(), diff --git a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/TandemMobiConnectionWizardActivity.kt b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/TandemMobiConnectionWizardActivity.kt index c838fed9d81..155c0861193 100644 --- a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/TandemMobiConnectionWizardActivity.kt +++ b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/TandemMobiConnectionWizardActivity.kt @@ -243,7 +243,8 @@ private fun WizardContent( } is WizardStep.Pairing -> { PairingScreen( - pairingStatus = state.pairingStatus + pairingStatus = state.pairingStatus, + pairingLabel = state.pairingLabel ) } is WizardStep.Error -> { @@ -262,6 +263,7 @@ private fun WizardContent( ConnectionCompleteScreen( pumpSerial = state.pairedPumpSerial, pumpName = state.pairedPumpName, + pumpApiVersion = state.pairedPumpApiVersion, onFinish = onFinish ) } diff --git a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/TandemMobiConnectionWizardViewModel.kt b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/TandemMobiConnectionWizardViewModel.kt index 41dd251b8d9..c72d9acd913 100644 --- a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/TandemMobiConnectionWizardViewModel.kt +++ b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/TandemMobiConnectionWizardViewModel.kt @@ -140,8 +140,11 @@ class TandemMobiConnectionWizardViewModel @Inject constructor( is EventTandemPairingStatus.WaitingForCode -> { _state.update { it.copy(pairingStatus = 40) } } - is EventTandemPairingStatus.Connecting -> { - _state.update { it.copy(pairingStatus = 50) } + is EventTandemPairingStatus.PairingInProgress -> { + _state.update { it.copy( + pairingStatus = 50 + (event.progressPercent / 2), + pairingLabel = event.progressLabel + ) } } is EventTandemPairingStatus.PairingSuccess -> { aapsLogger.info(LTag.PUMP, "Pairing successful: ${event.pumpSerial}") @@ -149,7 +152,8 @@ class TandemMobiConnectionWizardViewModel @Inject constructor( currentStep = WizardStep.Complete, pairingStatus = 100, pairedPumpSerial = event.pumpSerial, - pairedPumpName = event.pumpName + pairedPumpName = event.pumpName, + pairedPumpApiVersion = event.pumpApiVersion, )} } is EventTandemPairingStatus.PairingFailed -> { diff --git a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/TandemMobiWizardState.kt b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/TandemMobiWizardState.kt index cc7e1e0f7af..51e88fcb642 100644 --- a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/TandemMobiWizardState.kt +++ b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/TandemMobiWizardState.kt @@ -1,7 +1,9 @@ package app.aaps.pump.tandem.mobi.ui.wizard import android.bluetooth.BluetoothDevice +import app.aaps.pump.tandem.common.data.defs.TandemPumpApiVersion import app.aaps.pump.tandem.common.events.PairingError +import com.jwoglom.pumpx2.pump.messages.response.currentStatus.ApiVersionResponse /** * State for the Tandem Mobi connection wizard @@ -13,11 +15,13 @@ data class TandemMobiWizardState( val deviceName: String = "", val enteredPIN: String = "", val pairingStatus: Int = -1, + val pairingLabel: String = "", val pairingError: PairingError? = null, val retryCount: Int = 0, val isRePairing: Boolean = false, val pairedPumpSerial: String = "", - val pairedPumpName: String = "" + val pairedPumpName: String = "", + val pairedPumpApiVersion: TandemPumpApiVersion = TandemPumpApiVersion.Unknown ) /** diff --git a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/screens/ConnectionCompleteScreen.kt b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/screens/ConnectionCompleteScreen.kt index 7e9e9b31f2e..fa342e5fa5d 100644 --- a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/screens/ConnectionCompleteScreen.kt +++ b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/screens/ConnectionCompleteScreen.kt @@ -2,6 +2,7 @@ package app.aaps.pump.tandem.mobi.ui.wizard.screens import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -10,6 +11,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.Button import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -20,12 +22,15 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import app.aaps.pump.tandem.R +import app.aaps.pump.tandem.common.data.defs.TandemPumpApiVersion @Composable fun ConnectionCompleteScreen( pumpSerial: String, pumpName: String, + pumpApiVersion: TandemPumpApiVersion = TandemPumpApiVersion.Unknown, onFinish: () -> Unit, modifier: Modifier = Modifier ) { @@ -86,6 +91,15 @@ fun ConnectionCompleteScreen( text = pumpSerial, style = MaterialTheme.typography.bodyLarge ) + + Spacer(modifier = Modifier.height(12.dp)) + + Text( + text = stringResource(R.string.tandem_wizard_pump_supported_capabilities), + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + SupportedCapabilitiesForVersion(pumpApiVersion) } Spacer(modifier = Modifier.height(32.dp)) @@ -101,3 +115,49 @@ fun ConnectionCompleteScreen( } } } + +@Composable +fun SupportedCapabilitiesForVersion(apiVersion: TandemPumpApiVersion) { + + @Composable + fun IconAndText(ok: Boolean, text: String) { + Row( + modifier = Modifier.fillMaxWidth() + ) { + if (ok) { + Icon( + imageVector = Icons.Default.CheckCircle, + contentDescription = "Checkmark", + modifier = Modifier.size(16.dp), + tint = MaterialTheme.colorScheme.primary + ) + } else { + Icon( + imageVector = Icons.Default.Warning, + contentDescription = "Warning", + modifier = Modifier.size(16.dp), + tint = MaterialTheme.colorScheme.secondary + ) + + } + + Text( + text, + fontSize = 12.sp, + modifier = Modifier.height(16.dp), + ) + } + } + + Column( + modifier = Modifier.padding(horizontal = 16.dp) + ) { + if (apiVersion.isClosedLoopPossible) { + IconAndText(true, stringResource(R.string.tandem_wizard_pump_closed_loop_supported)) + } else if (apiVersion.hasBolus) { + IconAndText(true, stringResource(R.string.tandem_wizard_pump_bolus_only_supported)) + } else { + IconAndText(true, stringResource(R.string.tandem_wizard_pump_open_loop_only)) + } + } +} diff --git a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/screens/PairingScreen.kt b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/screens/PairingScreen.kt index ff3773d7d33..5a34bdd6272 100644 --- a/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/screens/PairingScreen.kt +++ b/pump/tandem/src/main/kotlin/app/aaps/pump/tandem/mobi/ui/wizard/screens/PairingScreen.kt @@ -21,6 +21,7 @@ import app.aaps.pump.tandem.R @Composable fun PairingScreen( pairingStatus: Int, + pairingLabel: String, modifier: Modifier = Modifier ) { Column( @@ -50,6 +51,14 @@ fun PairingScreen( textAlign = TextAlign.Center, color = MaterialTheme.colorScheme.onSurfaceVariant ) + + Text( + text = pairingLabel, + style = MaterialTheme.typography.bodySmall, + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } } diff --git a/pump/tandem/src/main/res/layout/tandem_mobi_fragment.xml b/pump/tandem/src/main/res/layout/tandem_mobi_fragment.xml index 59f8866c33c..5a5854f8d18 100755 --- a/pump/tandem/src/main/res/layout/tandem_mobi_fragment.xml +++ b/pump/tandem/src/main/res/layout/tandem_mobi_fragment.xml @@ -780,7 +780,7 @@ android:drawableTop="@drawable/ic_settings" android:paddingLeft="0dp" android:paddingRight="0dp" - android:text="@string/pump_settings" /> + android:text="@string/pump_actions" /> diff --git a/pump/tandem/src/main/res/values/strings.xml b/pump/tandem/src/main/res/values/strings.xml index afe6ed48bea..460c14bd4f5 100755 --- a/pump/tandem/src/main/res/values/strings.xml +++ b/pump/tandem/src/main/res/values/strings.xml @@ -19,7 +19,7 @@ %1$.2f %2$s (%3$s) %s (Open-Loop only) Ignored. Demo mode - Settings + Actions Data Driver Version Serial Number @@ -111,7 +111,7 @@ - Settings + Actions %1$d min left @@ -262,10 +262,14 @@ Max allowed Basal Rate is 250%% which is %1$.2f, with Base Basal Rate of %2$.2f. + Requested effective basal rate is %.2f U/h, which is below the minimum pump allowed non-zero basal rate of 0.1 U/h, so lowering basal rate to 0 U/h. Max configured Basal Rate on pump is %.2f U/h. Requested TBR was %d %%, which is more than pump allows (250%%), so limiting to 250%%. Requested TBR %% was bigger than max configured basal on pump %d. SMB Allowed on Mobi. + Bolus rounded down to 0 U (minimum bolus is %1$.2f U) + Bolus limited to %1$d U (pump max bolus setting) + Tandem Scan @@ -347,7 +351,7 @@ Pair Your Tandem Mobi Pump Re-pair Your Tandem Mobi Pump This wizard will guide you through pairing your Tandem Mobi pump with your phone via Bluetooth. - Prerequisites:\n• Pump is powered on\n• Pump wireless charger is available and plugged in\n• You have the 6-digit PIN from the pump + Prerequisites:\n• Pump is powered on\n• Pump has been placed on the wireless charger \n• You have the 6-digit PIN from the pump Begin Pairing @@ -387,6 +391,10 @@ Your Tandem Mobi pump has been paired successfully. Pump Name Serial Number + Supported Capabilities + Closed Loop supported + Open Loop with bolus supported (no closed loop) + Open Loop only (no closed loop) Finish