commit
7169c78225
15 changed files with 362 additions and 142 deletions
|
@ -42,6 +42,7 @@ import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin
|
||||||
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
|
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
|
||||||
import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin
|
import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin
|
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.OmnipodDashPumpPlugin
|
||||||
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
|
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
|
||||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
|
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
|
||||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
|
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
|
||||||
|
@ -154,11 +155,11 @@ abstract class PluginsModule {
|
||||||
@IntKey(150)
|
@IntKey(150)
|
||||||
abstract fun bindMedtronicPumpPlugin(plugin: MedtronicPumpPlugin): PluginBase
|
abstract fun bindMedtronicPumpPlugin(plugin: MedtronicPumpPlugin): PluginBase
|
||||||
|
|
||||||
// @Binds
|
@Binds
|
||||||
// @PumpDriver
|
@PumpDriver
|
||||||
// @IntoMap
|
@IntoMap
|
||||||
// @IntKey(155)
|
@IntKey(156)
|
||||||
// abstract fun bindOmnipodPumpPlugin(plugin: OmnipodErosPumpPlugin): PluginBase
|
abstract fun bindOmnipodDashPumpPlugin(plugin: OmnipodDashPumpPlugin): PluginBase
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@PumpDriver
|
@PumpDriver
|
||||||
|
@ -370,4 +371,4 @@ abstract class PluginsModule {
|
||||||
@Qualifier
|
@Qualifier
|
||||||
annotation class APS
|
annotation class APS
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.text.format.DateFormat
|
||||||
import dagger.android.HasAndroidInjector
|
import dagger.android.HasAndroidInjector
|
||||||
import info.nightscout.androidaps.activities.ErrorHelperActivity.Companion.runAlarm
|
import info.nightscout.androidaps.activities.ErrorHelperActivity.Companion.runAlarm
|
||||||
import info.nightscout.androidaps.data.DetailedBolusInfo
|
import info.nightscout.androidaps.data.DetailedBolusInfo
|
||||||
|
@ -11,6 +12,9 @@ import info.nightscout.androidaps.events.EventPreferenceChange
|
||||||
import info.nightscout.androidaps.events.EventProfileSwitchChanged
|
import info.nightscout.androidaps.events.EventProfileSwitchChanged
|
||||||
import info.nightscout.androidaps.events.EventRefreshOverview
|
import info.nightscout.androidaps.events.EventRefreshOverview
|
||||||
import info.nightscout.androidaps.events.EventTempBasalChange
|
import info.nightscout.androidaps.events.EventTempBasalChange
|
||||||
|
import info.nightscout.androidaps.extensions.convertedToAbsolute
|
||||||
|
import info.nightscout.androidaps.extensions.plannedRemainingMinutes
|
||||||
|
import info.nightscout.androidaps.extensions.toStringFull
|
||||||
import info.nightscout.androidaps.interfaces.*
|
import info.nightscout.androidaps.interfaces.*
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
import info.nightscout.androidaps.logging.LTag
|
import info.nightscout.androidaps.logging.LTag
|
||||||
|
@ -36,9 +40,12 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.BolusRe
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.BolusType
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.BolusType
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.TempBasalRecord
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.TempBasalRecord
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.OmnipodDashOverviewFragment
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.OmnipodDashOverviewFragment
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.Constants
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.mapProfileToBasalProgram
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.mapProfileToBasalProgram
|
||||||
import info.nightscout.androidaps.queue.commands.Command
|
import info.nightscout.androidaps.queue.commands.Command
|
||||||
import info.nightscout.androidaps.queue.commands.CustomCommand
|
import info.nightscout.androidaps.queue.commands.CustomCommand
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil
|
||||||
|
import info.nightscout.androidaps.utils.DecimalFormatter.to2Decimal
|
||||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||||
import info.nightscout.androidaps.utils.T
|
import info.nightscout.androidaps.utils.T
|
||||||
import info.nightscout.androidaps.utils.TimeChangeType
|
import info.nightscout.androidaps.utils.TimeChangeType
|
||||||
|
@ -49,8 +56,8 @@ import io.reactivex.Completable
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
import io.reactivex.rxkotlin.plusAssign
|
import io.reactivex.rxkotlin.plusAssign
|
||||||
import io.reactivex.rxkotlin.subscribeBy
|
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import java.lang.Exception
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.ZonedDateTime
|
import java.time.ZonedDateTime
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -73,6 +80,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val aapsSchedulers: AapsSchedulers,
|
private val aapsSchedulers: AapsSchedulers,
|
||||||
private val fabricPrivacy: FabricPrivacy,
|
private val fabricPrivacy: FabricPrivacy,
|
||||||
|
private val dateUtil: DateUtil,
|
||||||
|
|
||||||
injector: HasAndroidInjector,
|
injector: HasAndroidInjector,
|
||||||
aapsLogger: AAPSLogger,
|
aapsLogger: AAPSLogger,
|
||||||
|
@ -84,7 +92,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
|
|
||||||
private val handler: Handler = Handler(Looper.getMainLooper())
|
private val handler: Handler = Handler(Looper.getMainLooper())
|
||||||
private lateinit var statusChecker: Runnable
|
private lateinit var statusChecker: Runnable
|
||||||
var nextPodWarningCheck: Long = 0
|
private var nextPodWarningCheck: Long = 0
|
||||||
@Volatile var stopConnecting: CountDownLatch? = null
|
@Volatile var stopConnecting: CountDownLatch? = null
|
||||||
private var disposables: CompositeDisposable = CompositeDisposable()
|
private var disposables: CompositeDisposable = CompositeDisposable()
|
||||||
|
|
||||||
|
@ -127,8 +135,6 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
val tbr = expectedState.temporaryBasal
|
val tbr = expectedState.temporaryBasal
|
||||||
if (tbr == null || tbr.rate != 0.0) {
|
if (tbr == null || tbr.rate != 0.0) {
|
||||||
aapsLogger.info(LTag.PUMP, "createFakeTBRWhenNoActivePod")
|
aapsLogger.info(LTag.PUMP, "createFakeTBRWhenNoActivePod")
|
||||||
// calling connectNewPump() here because pumpSerial could have changed(from 4241 to "n/a")
|
|
||||||
pumpSync.connectNewPump()
|
|
||||||
pumpSync.syncTemporaryBasalWithPumpId(
|
pumpSync.syncTemporaryBasalWithPumpId(
|
||||||
timestamp = System.currentTimeMillis(),
|
timestamp = System.currentTimeMillis(),
|
||||||
rate = 0.0,
|
rate = 0.0,
|
||||||
|
@ -137,7 +143,9 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
|
type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
|
||||||
pumpId = Random.Default.nextLong(), // we don't use this, just make sure it's unique
|
pumpId = Random.Default.nextLong(), // we don't use this, just make sure it's unique
|
||||||
pumpType = PumpType.OMNIPOD_DASH,
|
pumpType = PumpType.OMNIPOD_DASH,
|
||||||
pumpSerial = serialNumber()
|
pumpSerial = Constants.PUMP_SERIAL_FOR_FAKE_TBR // switching the serialNumber here would need a
|
||||||
|
// call to connectNewPump. If we do that, then we will have a TBR started by the "n/a" pump and
|
||||||
|
// cancelled by "4241". This did not work ok.
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -510,7 +518,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
|
|
||||||
// Omnipod only reports reservoir level when there's < 1023 pulses left
|
// Omnipod only reports reservoir level when there's < 1023 pulses left
|
||||||
return podStateManager.pulsesRemaining?.let {
|
return podStateManager.pulsesRemaining?.let {
|
||||||
it * 0.05
|
it * PodConstants.POD_PULSE_BOLUS_UNITS
|
||||||
} ?: 75.0
|
} ?: 75.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -574,7 +582,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
).filter { podEvent -> podEvent.isCommandSent() }
|
).filter { podEvent -> podEvent.isCommandSent() }
|
||||||
.map { pumpSyncBolusStart(requestedBolusAmount, detailedBolusInfo.bolusType) }
|
.map { pumpSyncBolusStart(requestedBolusAmount, detailedBolusInfo.bolusType) }
|
||||||
.ignoreElements(),
|
.ignoreElements(),
|
||||||
post = waitForBolusDeliveryToComplete(BOLUS_RETRIES, requestedBolusAmount, detailedBolusInfo.bolusType)
|
post = waitForBolusDeliveryToComplete(requestedBolusAmount, detailedBolusInfo.bolusType)
|
||||||
.map {
|
.map {
|
||||||
deliveredBolusAmount = it
|
deliveredBolusAmount = it
|
||||||
aapsLogger.info(LTag.PUMP, "deliverTreatment: deliveredBolusAmount=$deliveredBolusAmount")
|
aapsLogger.info(LTag.PUMP, "deliverTreatment: deliveredBolusAmount=$deliveredBolusAmount")
|
||||||
|
@ -638,14 +646,13 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun waitForBolusDeliveryToComplete(
|
private fun waitForBolusDeliveryToComplete(
|
||||||
maxTries: Int,
|
|
||||||
requestedBolusAmount: Double,
|
requestedBolusAmount: Double,
|
||||||
bolusType: DetailedBolusInfo.BolusType
|
bolusType: DetailedBolusInfo.BolusType
|
||||||
): Single<Double> = Single.defer {
|
): Single<Double> = Single.defer {
|
||||||
|
|
||||||
if (bolusCanceled && podStateManager.activeCommand != null) {
|
if (bolusCanceled && podStateManager.activeCommand != null) {
|
||||||
var errorGettingStatus: Throwable? = null
|
var errorGettingStatus: Throwable? = null
|
||||||
for (tries in 1..maxTries) {
|
for (tries in 1..BOLUS_RETRIES) {
|
||||||
errorGettingStatus = getPodStatus().blockingGet()
|
errorGettingStatus = getPodStatus().blockingGet()
|
||||||
if (errorGettingStatus != null) {
|
if (errorGettingStatus != null) {
|
||||||
aapsLogger.debug(LTag.PUMP, "waitForBolusDeliveryToComplete errorGettingStatus=$errorGettingStatus")
|
aapsLogger.debug(LTag.PUMP, "waitForBolusDeliveryToComplete errorGettingStatus=$errorGettingStatus")
|
||||||
|
@ -676,7 +683,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (tryNumber in 1..maxTries) {
|
for (tryNumber in 1..BOLUS_RETRIES) {
|
||||||
updateBolusProgressDialog("Checking delivery status", 100)
|
updateBolusProgressDialog("Checking delivery status", 100)
|
||||||
|
|
||||||
val cmd = if (bolusCanceled)
|
val cmd = if (bolusCanceled)
|
||||||
|
@ -724,13 +731,15 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun estimateBolusDeliverySeconds(requestedBolusAmount: Double): Long {
|
private fun estimateBolusDeliverySeconds(requestedBolusAmount: Double): Long {
|
||||||
return ceil(requestedBolusAmount / 0.05).toLong() * 2 + 3
|
return ceil(requestedBolusAmount / PodConstants.POD_PULSE_BOLUS_UNITS).toLong() * 2 + 3
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun pumpSyncBolusStart(
|
private fun pumpSyncBolusStart(
|
||||||
requestedBolusAmount: Double,
|
requestedBolusAmount: Double,
|
||||||
bolusType: DetailedBolusInfo.BolusType
|
bolusType: DetailedBolusInfo.BolusType
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
require(requestedBolusAmount > 0) { "requestedBolusAmount has to be positive" }
|
||||||
|
|
||||||
val activeCommand = podStateManager.activeCommand
|
val activeCommand = podStateManager.activeCommand
|
||||||
if (activeCommand == null) {
|
if (activeCommand == null) {
|
||||||
throw IllegalArgumentException(
|
throw IllegalArgumentException(
|
||||||
|
@ -776,7 +785,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
)
|
)
|
||||||
|
|
||||||
val ret = executeProgrammingCommand(
|
val ret = executeProgrammingCommand(
|
||||||
pre = observeNoActiveTempBasal(true),
|
pre = observeNoActiveTempBasal(),
|
||||||
historyEntry = history.createRecord(
|
historyEntry = history.createRecord(
|
||||||
commandType = OmnipodCommandType.SET_TEMPORARY_BASAL,
|
commandType = OmnipodCommandType.SET_TEMPORARY_BASAL,
|
||||||
tempBasalRecord = TempBasalRecord(duration = durationInMinutes, rate = absoluteRate)
|
tempBasalRecord = TempBasalRecord(duration = durationInMinutes, rate = absoluteRate)
|
||||||
|
@ -846,38 +855,28 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeNoActiveTempBasal(enforceNew: Boolean): Completable {
|
private fun observeNoActiveTempBasal(): Completable {
|
||||||
return Completable.defer {
|
return Completable.defer {
|
||||||
when {
|
if (podStateManager.deliveryStatus !in
|
||||||
podStateManager.deliveryStatus !in
|
arrayOf(DeliveryStatus.TEMP_BASAL_ACTIVE, DeliveryStatus.BOLUS_AND_TEMP_BASAL_ACTIVE)
|
||||||
arrayOf(DeliveryStatus.TEMP_BASAL_ACTIVE, DeliveryStatus.BOLUS_AND_TEMP_BASAL_ACTIVE) -> {
|
) {
|
||||||
// TODO: what happens if we try to cancel nonexistent temp basal?
|
// TODO: what happens if we try to cancel nonexistent temp basal?
|
||||||
aapsLogger.info(LTag.PUMP, "No temporary basal to cancel")
|
aapsLogger.info(LTag.PUMP, "No temporary basal to cancel")
|
||||||
Completable.complete()
|
Completable.complete()
|
||||||
}
|
} else {
|
||||||
|
// enforceNew == true
|
||||||
!enforceNew ->
|
aapsLogger.info(LTag.PUMP, "Canceling existing temp basal")
|
||||||
Completable.error(
|
executeProgrammingCommand(
|
||||||
IllegalStateException(
|
historyEntry = history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL),
|
||||||
"Temporary basal already active and enforceNew is not set."
|
command = omnipodManager.stopTempBasal(hasTempBasalBeepEnabled()).ignoreElements()
|
||||||
)
|
).doOnComplete {
|
||||||
|
notifyOnUnconfirmed(
|
||||||
|
Notification.OMNIPOD_TBR_ALERTS,
|
||||||
|
"Cancelling temp basal might have failed." +
|
||||||
|
"If a temp basal was previously running, it might have been cancelled." +
|
||||||
|
"Please manually refresh the Pod status from the Omnipod tab.", // TODO: i8n
|
||||||
|
R.raw.boluserror,
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> {
|
|
||||||
// enforceNew == true
|
|
||||||
aapsLogger.info(LTag.PUMP, "Canceling existing temp basal")
|
|
||||||
executeProgrammingCommand(
|
|
||||||
historyEntry = history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL),
|
|
||||||
command = omnipodManager.stopTempBasal(hasTempBasalBeepEnabled()).ignoreElements()
|
|
||||||
).doOnComplete {
|
|
||||||
notifyOnUnconfirmed(
|
|
||||||
Notification.OMNIPOD_TBR_ALERTS,
|
|
||||||
"Cancelling temp basal might have failed." +
|
|
||||||
"If a temp basal was previously running, it might have been cancelled." +
|
|
||||||
"Please manually refresh the Pod status from the Omnipod tab.", // TODO: i8n
|
|
||||||
R.raw.boluserror,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -952,8 +951,53 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject {
|
override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject {
|
||||||
// TODO
|
val now = System.currentTimeMillis()
|
||||||
return JSONObject()
|
if (podStateManager.lastUpdatedSystem + 60 * 60 * 1000L < now) {
|
||||||
|
return JSONObject()
|
||||||
|
}
|
||||||
|
val pumpJson = JSONObject()
|
||||||
|
val status = JSONObject()
|
||||||
|
val extended = JSONObject()
|
||||||
|
try {
|
||||||
|
val podStatus = when {
|
||||||
|
podStateManager.isPodRunning && podStateManager.isSuspended ->
|
||||||
|
"suspended"
|
||||||
|
podStateManager.isPodRunning ->
|
||||||
|
"normal"
|
||||||
|
else ->
|
||||||
|
"no active Pod"
|
||||||
|
}
|
||||||
|
status.put("status", podStatus)
|
||||||
|
status.put("timestamp", dateUtil.toISOString(podStateManager.lastUpdatedSystem))
|
||||||
|
|
||||||
|
extended.put("Version", version)
|
||||||
|
try {
|
||||||
|
extended.put("ActiveProfile", profileName)
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
}
|
||||||
|
val tb = pumpSync.expectedPumpState().temporaryBasal
|
||||||
|
tb?.run {
|
||||||
|
extended.put("TempBasalAbsoluteRate", this.convertedToAbsolute(now, profile))
|
||||||
|
extended.put("TempBasalStart", dateUtil.dateAndTimeString(this.timestamp))
|
||||||
|
extended.put("TempBasalRemaining", this.plannedRemainingMinutes)
|
||||||
|
}
|
||||||
|
podStateManager.lastBolus?.run {
|
||||||
|
extended.put("LastBolus", dateUtil.dateAndTimeString(this.startTime))
|
||||||
|
extended.put("LastBolusAmount", this.deliveredUnits() ?: this.requestedUnits)
|
||||||
|
}
|
||||||
|
extended.put("BaseBasalRate", baseBasalRate)
|
||||||
|
|
||||||
|
pumpJson.put("status", status)
|
||||||
|
pumpJson.put("extended", extended)
|
||||||
|
if (podStateManager.pulsesRemaining == null) {
|
||||||
|
pumpJson.put("reservoir_display_override", "50+")
|
||||||
|
}
|
||||||
|
pumpJson.put("reservoir", reservoirLevel.toInt())
|
||||||
|
pumpJson.put("clock", dateUtil.toISOString(now))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
aapsLogger.error(LTag.PUMP, "Unhandled exception: $e")
|
||||||
|
}
|
||||||
|
return pumpJson
|
||||||
}
|
}
|
||||||
|
|
||||||
override val pumpDescription: PumpDescription = Companion.pumpDescription
|
override val pumpDescription: PumpDescription = Companion.pumpDescription
|
||||||
|
@ -972,8 +1016,33 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun shortStatus(veryShort: Boolean): String {
|
override fun shortStatus(veryShort: Boolean): String {
|
||||||
// TODO
|
if (!podStateManager.isActivationCompleted) {
|
||||||
return "TODO"
|
return resourceHelper.gs(R.string.omnipod_common_short_status_no_active_pod)
|
||||||
|
}
|
||||||
|
var ret = ""
|
||||||
|
if (podStateManager.lastUpdatedSystem != 0L) {
|
||||||
|
val agoMsec: Long = System.currentTimeMillis() - podStateManager.lastUpdatedSystem
|
||||||
|
val agoMin = (agoMsec / 60.0 / 1000.0).toInt()
|
||||||
|
ret += resourceHelper.gs(R.string.omnipod_common_short_status_last_connection, agoMin) + "\n"
|
||||||
|
}
|
||||||
|
podStateManager.lastBolus?.run {
|
||||||
|
ret += resourceHelper.gs(
|
||||||
|
R.string.omnipod_common_short_status_last_bolus, to2Decimal(this.deliveredUnits() ?: this.requestedUnits),
|
||||||
|
DateFormat.format("HH:mm", Date(this.startTime))
|
||||||
|
) + "\n"
|
||||||
|
}
|
||||||
|
val (temporaryBasal, extendedBolus, _, profile) = pumpSync.expectedPumpState()
|
||||||
|
temporaryBasal?.run {
|
||||||
|
ret += resourceHelper.gs(
|
||||||
|
R.string.omnipod_common_short_status_temp_basal,
|
||||||
|
this.toStringFull(dateUtil)
|
||||||
|
) + "\n"
|
||||||
|
}
|
||||||
|
ret += resourceHelper.gs(
|
||||||
|
R.string.omnipod_common_short_status_reservoir,
|
||||||
|
podStateManager.pulsesRemaining?.let { reservoirLevel.toString() } ?: "50+"
|
||||||
|
)
|
||||||
|
return ret.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val isFakingTempsByExtendedBoluses: Boolean
|
override val isFakingTempsByExtendedBoluses: Boolean
|
||||||
|
@ -1346,7 +1415,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
pumpId = historyEntry.pumpId(),
|
pumpId = historyEntry.pumpId(),
|
||||||
pumpType = PumpType.OMNIPOD_DASH,
|
pumpType = PumpType.OMNIPOD_DASH,
|
||||||
pumpSerial = serialNumber(),
|
pumpSerial = serialNumber(),
|
||||||
type = null // TODO: set the correct bolus type here!!!
|
type = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
rxBus.send(EventDismissNotification(Notification.OMNIPOD_UNCERTAIN_SMB))
|
rxBus.send(EventDismissNotification(Notification.OMNIPOD_UNCERTAIN_SMB))
|
||||||
|
@ -1356,6 +1425,10 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
if (confirmation.success) {
|
if (confirmation.success) {
|
||||||
podStateManager.lastBolus?.run {
|
podStateManager.lastBolus?.run {
|
||||||
val deliveredUnits = markComplete()
|
val deliveredUnits = markComplete()
|
||||||
|
if (deliveredUnits < 0) {
|
||||||
|
aapsLogger.error(LTag.PUMP, "Negative delivered units!!! $deliveredUnits")
|
||||||
|
return
|
||||||
|
}
|
||||||
val bolusHistoryEntry = history.getById(historyId)
|
val bolusHistoryEntry = history.getById(historyId)
|
||||||
val sync = pumpSync.syncBolusWithPumpId(
|
val sync = pumpSync.syncBolusWithPumpId(
|
||||||
timestamp = bolusHistoryEntry.createdAt,
|
timestamp = bolusHistoryEntry.createdAt,
|
||||||
|
|
|
@ -9,6 +9,9 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.*
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.GetVersionCommand.Companion.DEFAULT_UNIQUE_ID
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.GetVersionCommand.Companion.DEFAULT_UNIQUE_ID
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.*
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.*
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.MAX_POD_LIFETIME
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.MAX_POD_LIFETIME
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.POD_EXPIRATION_ALERT_HOURS
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.POD_EXPIRATION_ALERT_HOURS_DURATION
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.POD_EXPIRATION_IMMINENT_ALERT_HOURS
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.*
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.*
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
|
||||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||||
|
@ -278,7 +281,7 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
.setUniqueId(podStateManager.uniqueId!!.toInt())
|
.setUniqueId(podStateManager.uniqueId!!.toInt())
|
||||||
.setSequenceNumber(podStateManager.messageSequenceNumber)
|
.setSequenceNumber(podStateManager.messageSequenceNumber)
|
||||||
.setNonce(NONCE)
|
.setNonce(NONCE)
|
||||||
.setNumberOfUnits(podStateManager.firstPrimeBolusVolume!! * 0.05)
|
.setNumberOfUnits(podStateManager.firstPrimeBolusVolume!! * PodConstants.POD_PULSE_BOLUS_UNITS)
|
||||||
.setDelayBetweenPulsesInEighthSeconds(podStateManager.primePulseRate!!.toByte())
|
.setDelayBetweenPulsesInEighthSeconds(podStateManager.primePulseRate!!.toByte())
|
||||||
.setProgramReminder(ProgramReminder(atStart = false, atEnd = false, atInterval = 0))
|
.setProgramReminder(ProgramReminder(atStart = false, atEnd = false, atInterval = 0))
|
||||||
.build(),
|
.build(),
|
||||||
|
@ -380,7 +383,7 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
)
|
)
|
||||||
observables.add(
|
observables.add(
|
||||||
observeSendProgramBolusCommand(
|
observeSendProgramBolusCommand(
|
||||||
podStateManager.secondPrimeBolusVolume!! * 0.05,
|
podStateManager.secondPrimeBolusVolume!! * PodConstants.POD_PULSE_BOLUS_UNITS,
|
||||||
podStateManager.primePulseRate!!.toByte(),
|
podStateManager.primePulseRate!!.toByte(),
|
||||||
confirmationBeeps = false,
|
confirmationBeeps = false,
|
||||||
completionBeeps = false
|
completionBeeps = false
|
||||||
|
@ -394,10 +397,10 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
AlertConfiguration(
|
AlertConfiguration(
|
||||||
AlertType.EXPIRATION,
|
AlertType.EXPIRATION,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
durationInMinutes = TimeUnit.HOURS.toMinutes(7).toShort(),
|
durationInMinutes = TimeUnit.HOURS.toMinutes(POD_EXPIRATION_ALERT_HOURS_DURATION).toShort(),
|
||||||
autoOff = false,
|
autoOff = false,
|
||||||
AlertTrigger.TimerTrigger(
|
AlertTrigger.TimerTrigger(
|
||||||
TimeUnit.HOURS.toMinutes(72).toShort()
|
TimeUnit.HOURS.toMinutes(POD_EXPIRATION_ALERT_HOURS).toShort()
|
||||||
), // FIXME use activation time
|
), // FIXME use activation time
|
||||||
BeepType.FOUR_TIMES_BIP_BEEP,
|
BeepType.FOUR_TIMES_BIP_BEEP,
|
||||||
BeepRepetitionType.XXX3
|
BeepRepetitionType.XXX3
|
||||||
|
@ -408,7 +411,7 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
durationInMinutes = 0,
|
durationInMinutes = 0,
|
||||||
autoOff = false,
|
autoOff = false,
|
||||||
AlertTrigger.TimerTrigger(
|
AlertTrigger.TimerTrigger(
|
||||||
TimeUnit.HOURS.toMinutes(79).toShort()
|
TimeUnit.HOURS.toMinutes(POD_EXPIRATION_IMMINENT_ALERT_HOURS).toShort()
|
||||||
), // FIXME use activation time
|
), // FIXME use activation time
|
||||||
BeepType.FOUR_TIMES_BIP_BEEP,
|
BeepType.FOUR_TIMES_BIP_BEEP,
|
||||||
BeepRepetitionType.XXX4
|
BeepRepetitionType.XXX4
|
||||||
|
|
|
@ -4,5 +4,5 @@ import info.nightscout.androidaps.extensions.toHex
|
||||||
|
|
||||||
class IncorrectPacketException(
|
class IncorrectPacketException(
|
||||||
val payload: ByteArray,
|
val payload: ByteArray,
|
||||||
val expectedIndex: Byte? = null
|
private val expectedIndex: Byte? = null
|
||||||
) : Exception("Invalid payload: ${payload.toHex()}. Expected index: $expectedIndex")
|
) : Exception("Invalid payload: ${payload.toHex()}. Expected index: $expectedIndex")
|
||||||
|
|
|
@ -29,7 +29,7 @@ class MessageIO(
|
||||||
private val dataBleIO: DataBleIO,
|
private val dataBleIO: DataBleIO,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val receivedOutOfOrder = LinkedHashMap<Byte, ByteArray>()
|
private val receivedOutOfOrder = LinkedHashMap<Byte, ByteArray>()
|
||||||
var maxMessageReadTries = 3
|
var maxMessageReadTries = 3
|
||||||
var messageReadTries = 0
|
var messageReadTries = 0
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message
|
||||||
|
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotParseMessageException
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotParseMessageException
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.Flag
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
@ -80,13 +81,13 @@ data class MessagePacket(
|
||||||
if (payload.copyOfRange(0, 2).decodeToString() != MAGIC_PATTERN) {
|
if (payload.copyOfRange(0, 2).decodeToString() != MAGIC_PATTERN) {
|
||||||
throw CouldNotParseMessageException(payload)
|
throw CouldNotParseMessageException(payload)
|
||||||
}
|
}
|
||||||
val f1 = Flag(payload[2].toInt())
|
val f1 = Flag(payload[2].toInt() and 0xff)
|
||||||
val sas = f1.get(3) != 0
|
val sas = f1.get(3) != 0
|
||||||
val tfs = f1.get(4) != 0
|
val tfs = f1.get(4) != 0
|
||||||
val version = ((f1.get(0) shl 2) or (f1.get(1) shl 1) or (f1.get(2) shl 0)).toShort()
|
val version = ((f1.get(0) shl 2) or (f1.get(1) shl 1) or (f1.get(2) shl 0)).toShort()
|
||||||
val eqos = (f1.get(7) or (f1.get(6) shl 1) or (f1.get(5) shl 2)).toShort()
|
val eqos = (f1.get(7) or (f1.get(6) shl 1) or (f1.get(5) shl 2)).toShort()
|
||||||
|
|
||||||
val f2 = Flag(payload[3].toInt())
|
val f2 = Flag(payload[3].toInt() and 0xff)
|
||||||
val ack = f2.get(0) != 0
|
val ack = f2.get(0) != 0
|
||||||
val priority = f2.get(1) != 0
|
val priority = f2.get(1) != 0
|
||||||
val lastMessage = f2.get(2) != 0
|
val lastMessage = f2.get(2) != 0
|
||||||
|
@ -125,24 +126,6 @@ data class MessagePacket(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Flag(var value: Int = 0) {
|
|
||||||
|
|
||||||
fun set(idx: Byte, set: Boolean) {
|
|
||||||
val mask = 1 shl (7 - idx)
|
|
||||||
if (!set)
|
|
||||||
return
|
|
||||||
value = value or mask
|
|
||||||
}
|
|
||||||
|
|
||||||
fun get(idx: Byte): Int {
|
|
||||||
val mask = 1 shl (7 - idx)
|
|
||||||
if (value and mask == 0) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Byte.toUnsignedInt() = this.toInt() and 0xff
|
internal fun Byte.toUnsignedInt() = this.toInt() and 0xff
|
||||||
|
|
||||||
private fun ByteArray.assertSizeAtLeast(size: Int) {
|
private fun ByteArray.assertSizeAtLeast(size: Int) {
|
||||||
|
|
|
@ -4,6 +4,19 @@ import java.time.Duration
|
||||||
|
|
||||||
class PodConstants {
|
class PodConstants {
|
||||||
companion object {
|
companion object {
|
||||||
val MAX_POD_LIFETIME = Duration.ofHours(80)
|
val MAX_POD_LIFETIME: Duration = Duration.ofHours(80)
|
||||||
|
|
||||||
|
// Expiration alert time in minutes since activation and duration in minutes
|
||||||
|
const val POD_EXPIRATION_ALERT_HOURS = 72L
|
||||||
|
const val POD_EXPIRATION_ALERT_HOURS_DURATION = 7L
|
||||||
|
|
||||||
|
// Expiration eminent alert time in minutes since activation
|
||||||
|
const val POD_EXPIRATION_IMMINENT_ALERT_HOURS = 79L
|
||||||
|
|
||||||
|
// Bolus & Priming units
|
||||||
|
const val POD_PULSE_BOLUS_UNITS = 0.05
|
||||||
|
|
||||||
|
// Reservoir units alert threshold
|
||||||
|
const val DEFAULT_MAX_RESERVOIR_ALERT_THRESHOLD: Short = 20
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definitio
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.AlertUtil
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.AlertUtil
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.byValue
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.byValue
|
||||||
|
import java.nio.ByteBuffer
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.experimental.and
|
import kotlin.experimental.and
|
||||||
|
|
||||||
|
@ -13,18 +14,21 @@ class DefaultStatusResponse(
|
||||||
) : ResponseBase(ResponseType.DEFAULT_STATUS_RESPONSE, encoded) {
|
) : ResponseBase(ResponseType.DEFAULT_STATUS_RESPONSE, encoded) {
|
||||||
|
|
||||||
val messageType: Byte = encoded[0]
|
val messageType: Byte = encoded[0]
|
||||||
val deliveryStatus: DeliveryStatus = byValue((encoded[1].toInt() shr 4 and 0x0f).toByte(), DeliveryStatus.UNKNOWN)
|
|
||||||
val podStatus: PodStatus = byValue((encoded[1] and 0x0f), PodStatus.UNKNOWN)
|
|
||||||
val totalPulsesDelivered: Short =
|
|
||||||
(encoded[2] and 0x0f shl 9 or (encoded[3].toInt() and 0xff shl 1) or (encoded[4].toInt() and 0xff ushr 7)).toShort()
|
|
||||||
|
|
||||||
val sequenceNumberOfLastProgrammingCommand: Short = (encoded[4] ushr 3 and 0x0f).toShort()
|
private var first4bytes = ByteBuffer.wrap(byteArrayOf(encoded[2], encoded[3], encoded[4], encoded[5])).int
|
||||||
val bolusPulsesRemaining: Short = ((encoded[4] and 0x07 shl 10 or (encoded[5].toInt() and 0xff) and 2047).toShort())
|
private var last4bytes = ByteBuffer.wrap(byteArrayOf(encoded[6], encoded[7], encoded[8], encoded[9])).int
|
||||||
|
|
||||||
|
val podStatus: PodStatus = byValue((encoded[1] and 0x0f), PodStatus.UNKNOWN)
|
||||||
|
val deliveryStatus: DeliveryStatus = byValue(((encoded[1].toInt() and 0xff) shr 4 and 0x0f).toByte(), DeliveryStatus.UNKNOWN)
|
||||||
|
|
||||||
|
val totalPulsesDelivered: Short = (first4bytes ushr 11 ushr 4 and 0x1FFF).toShort()
|
||||||
|
val sequenceNumberOfLastProgrammingCommand: Short = (first4bytes ushr 11 and 0X0F).toShort()
|
||||||
|
val bolusPulsesRemaining: Short = (first4bytes and 0X7FF).toShort()
|
||||||
|
|
||||||
val activeAlerts: EnumSet<AlertType> =
|
val activeAlerts: EnumSet<AlertType> =
|
||||||
AlertUtil.decodeAlertSet((encoded[6].toInt() and 0xff shl 1 or (encoded[7] ushr 7)).toByte())
|
AlertUtil.decodeAlertSet((last4bytes ushr 10 ushr 13 and 0xFF).toByte())
|
||||||
val minutesSinceActivation: Short =
|
val minutesSinceActivation: Short = ((last4bytes ushr 10 and 0x1FFF)).toShort()
|
||||||
(encoded[7] and 0x7f shl 6 or (encoded[8].toInt() and 0xff ushr 2 and 0x3f)).toShort()
|
val reservoirPulsesRemaining: Short = (last4bytes and 0X3FF).toShort()
|
||||||
val reservoirPulsesRemaining: Short = (encoded[8] shl 8 or encoded[9].toInt() and 0x3ff).toShort()
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "DefaultStatusResponse(" +
|
return "DefaultStatusResponse(" +
|
||||||
|
|
|
@ -311,7 +311,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
|
|
||||||
private fun updateLastBolusFromResponse(bolusPulsesRemaining: Short) {
|
private fun updateLastBolusFromResponse(bolusPulsesRemaining: Short) {
|
||||||
podState.lastBolus?.run {
|
podState.lastBolus?.run {
|
||||||
val remainingUnits = bolusPulsesRemaining.toDouble() * 0.05
|
val remainingUnits = bolusPulsesRemaining.toDouble() * PodConstants.POD_PULSE_BOLUS_UNITS
|
||||||
this.bolusUnitsRemaining = remainingUnits
|
this.bolusUnitsRemaining = remainingUnits
|
||||||
if (remainingUnits == 0.0) {
|
if (remainingUnits == 0.0) {
|
||||||
this.deliveryComplete = true
|
this.deliveryComplete = true
|
||||||
|
@ -627,11 +627,13 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun connectionSuccessRatio(): Float {
|
override fun connectionSuccessRatio(): Float {
|
||||||
val attempts = connectionAttempts
|
if (connectionAttempts == 0) {
|
||||||
if (attempts == 0) {
|
|
||||||
return 0.0F
|
return 0.0F
|
||||||
|
} else if (connectionAttempts <= successfulConnections) {
|
||||||
|
// Prevent bogus quality > 1 during initialisation
|
||||||
|
return 1.0F
|
||||||
}
|
}
|
||||||
return successfulConnections.toFloat() / attempts.toFloat()
|
return successfulConnections.toFloat() / connectionAttempts.toFloat()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun reset() {
|
override fun reset() {
|
||||||
|
|
|
@ -32,6 +32,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.databinding.OmnipodD
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.databinding.OmnipodDashOverviewBluetoothStatusBinding
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.databinding.OmnipodDashOverviewBluetoothStatusBinding
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ActivationProgress
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ActivationProgress
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertType
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertType
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
|
||||||
import info.nightscout.androidaps.queue.Callback
|
import info.nightscout.androidaps.queue.Callback
|
||||||
import info.nightscout.androidaps.queue.events.EventQueueChanged
|
import info.nightscout.androidaps.queue.events.EventQueueChanged
|
||||||
|
@ -68,7 +69,6 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
@Inject lateinit var buildHelper: BuildHelper
|
@Inject lateinit var buildHelper: BuildHelper
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private const val REFRESH_INTERVAL_MILLIS = 15 * 1000L // 15 seconds
|
private const val REFRESH_INTERVAL_MILLIS = 15 * 1000L // 15 seconds
|
||||||
private const val PLACEHOLDER = "-"
|
private const val PLACEHOLDER = "-"
|
||||||
private const val MAX_TIME_DEVIATION_MINUTES = 10L
|
private const val MAX_TIME_DEVIATION_MINUTES = 10L
|
||||||
|
@ -372,7 +372,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
if (podStateManager.isActivationCompleted && podStateManager.pulsesDelivered != null) {
|
if (podStateManager.isActivationCompleted && podStateManager.pulsesDelivered != null) {
|
||||||
resourceHelper.gs(
|
resourceHelper.gs(
|
||||||
R.string.omnipod_common_overview_total_delivered_value,
|
R.string.omnipod_common_overview_total_delivered_value,
|
||||||
podStateManager.pulsesDelivered!! * 0.05
|
(podStateManager.pulsesDelivered!! * PodConstants.POD_PULSE_BOLUS_UNITS)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
PLACEHOLDER
|
PLACEHOLDER
|
||||||
|
@ -387,11 +387,11 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
// TODO
|
// TODO
|
||||||
// val lowReservoirThreshold = (omnipodAlertUtil.lowReservoirAlertUnits
|
// val lowReservoirThreshold = (omnipodAlertUtil.lowReservoirAlertUnits
|
||||||
// ?: OmnipodConstants.DEFAULT_MAX_RESERVOIR_ALERT_THRESHOLD).toDouble()
|
// ?: OmnipodConstants.DEFAULT_MAX_RESERVOIR_ALERT_THRESHOLD).toDouble()
|
||||||
val lowReservoirThreshold: Short = 20
|
val lowReservoirThreshold: Short = PodConstants.DEFAULT_MAX_RESERVOIR_ALERT_THRESHOLD
|
||||||
|
|
||||||
podInfoBinding.reservoir.text = resourceHelper.gs(
|
podInfoBinding.reservoir.text = resourceHelper.gs(
|
||||||
R.string.omnipod_common_overview_reservoir_value,
|
R.string.omnipod_common_overview_reservoir_value,
|
||||||
(podStateManager.pulsesRemaining!! * 0.05)
|
(podStateManager.pulsesRemaining!! * PodConstants.POD_PULSE_BOLUS_UNITS)
|
||||||
)
|
)
|
||||||
podInfoBinding.reservoir.setTextColor(
|
podInfoBinding.reservoir.setTextColor(
|
||||||
if (podStateManager.pulsesRemaining!! < lowReservoirThreshold) {
|
if (podStateManager.pulsesRemaining!! < lowReservoirThreshold) {
|
||||||
|
|
|
@ -14,8 +14,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.I8n
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
|
||||||
import io.reactivex.rxkotlin.addTo
|
|
||||||
import io.reactivex.rxkotlin.plusAssign
|
import io.reactivex.rxkotlin.plusAssign
|
||||||
import io.reactivex.rxkotlin.subscribeBy
|
import io.reactivex.rxkotlin.subscribeBy
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
|
@ -16,13 +16,12 @@ import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activati
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.R
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.R
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.Constants
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.I8n
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.I8n
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.mapProfileToBasalProgram
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.mapProfileToBasalProgram
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
|
||||||
import io.reactivex.rxkotlin.addTo
|
|
||||||
import io.reactivex.rxkotlin.plusAssign
|
import io.reactivex.rxkotlin.plusAssign
|
||||||
import io.reactivex.rxkotlin.subscribeBy
|
import io.reactivex.rxkotlin.subscribeBy
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -67,43 +66,48 @@ class DashInsertCannulaViewModel @Inject constructor(
|
||||||
|
|
||||||
super.disposable += omnipodManager.activatePodPart2(basalProgram, expirationHoursBeforeShutdown)
|
super.disposable += omnipodManager.activatePodPart2(basalProgram, expirationHoursBeforeShutdown)
|
||||||
.subscribeBy(
|
.subscribeBy(
|
||||||
onNext = { podEvent ->
|
onNext = { podEvent ->
|
||||||
logger.debug(
|
logger.debug(
|
||||||
LTag.PUMP,
|
LTag.PUMP,
|
||||||
"Received PodEvent in Pod activation part 2: $podEvent"
|
"Received PodEvent in Pod activation part 2: $podEvent"
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onError = { throwable ->
|
onError = { throwable ->
|
||||||
logger.error(LTag.PUMP, "Error in Pod activation part 2", throwable)
|
logger.error(LTag.PUMP, "Error in Pod activation part 2", throwable)
|
||||||
source.onSuccess(PumpEnactResult(injector).success(false).comment(I8n.textFromException(throwable, resourceHelper)))
|
source.onSuccess(PumpEnactResult(injector).success(false).comment(I8n.textFromException(throwable, resourceHelper)))
|
||||||
},
|
},
|
||||||
onComplete = {
|
onComplete = {
|
||||||
logger.debug("Pod activation part 2 completed")
|
logger.debug("Pod activation part 2 completed")
|
||||||
podStateManager.basalProgram = basalProgram
|
podStateManager.basalProgram = basalProgram
|
||||||
pumpSync.connectNewPump()
|
|
||||||
pumpSync.insertTherapyEventIfNewWithTimestamp(
|
pumpSync.syncStopTemporaryBasalWithPumpId(
|
||||||
timestamp = System.currentTimeMillis(),
|
timestamp = System.currentTimeMillis(),
|
||||||
type = DetailedBolusInfo.EventType.CANNULA_CHANGE,
|
endPumpId = System.currentTimeMillis(),
|
||||||
pumpType = PumpType.OMNIPOD_DASH,
|
pumpType = PumpType.OMNIPOD_DASH,
|
||||||
pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a"
|
pumpSerial = Constants.PUMP_SERIAL_FOR_FAKE_TBR // cancel the fake TBR with the same pump
|
||||||
)
|
// serial that it was created with
|
||||||
pumpSync.insertTherapyEventIfNewWithTimestamp(
|
)
|
||||||
timestamp = System.currentTimeMillis(),
|
|
||||||
type = DetailedBolusInfo.EventType.INSULIN_CHANGE,
|
pumpSync.connectNewPump()
|
||||||
pumpType = PumpType.OMNIPOD_DASH,
|
|
||||||
pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a"
|
pumpSync.insertTherapyEventIfNewWithTimestamp(
|
||||||
)
|
timestamp = System.currentTimeMillis(),
|
||||||
pumpSync.syncStopTemporaryBasalWithPumpId(
|
type = DetailedBolusInfo.EventType.CANNULA_CHANGE,
|
||||||
timestamp = System.currentTimeMillis(),
|
pumpType = PumpType.OMNIPOD_DASH,
|
||||||
endPumpId = System.currentTimeMillis(),
|
pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a"
|
||||||
pumpType = PumpType.OMNIPOD_DASH,
|
)
|
||||||
pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a"
|
pumpSync.insertTherapyEventIfNewWithTimestamp(
|
||||||
)
|
timestamp = System.currentTimeMillis(),
|
||||||
podStateManager.updateExpirationAlertSettings(expirationReminderEnabled, expirationHours)
|
type = DetailedBolusInfo.EventType.INSULIN_CHANGE,
|
||||||
rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_NOT_ATTACHED))
|
pumpType = PumpType.OMNIPOD_DASH,
|
||||||
source.onSuccess(PumpEnactResult(injector).success(true))
|
pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a"
|
||||||
}
|
)
|
||||||
)
|
|
||||||
|
podStateManager.updateExpirationAlertSettings(expirationReminderEnabled, expirationHours)
|
||||||
|
rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_NOT_ATTACHED))
|
||||||
|
source.onSuccess(PumpEnactResult(injector).success(true))
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.util
|
||||||
|
|
||||||
|
class Constants {
|
||||||
|
companion object {
|
||||||
|
const val PUMP_SERIAL_FOR_FAKE_TBR = "4241"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.util
|
||||||
|
|
||||||
|
class Flag(var value: Int = 0) {
|
||||||
|
|
||||||
|
fun set(idx: Byte, set: Boolean) {
|
||||||
|
val mask = 1 shl (7 - idx)
|
||||||
|
if (!set)
|
||||||
|
return
|
||||||
|
value = value or mask
|
||||||
|
}
|
||||||
|
|
||||||
|
fun get(idx: Byte): Int {
|
||||||
|
val mask = 1 shl (7 - idx)
|
||||||
|
if (value and mask == 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertType
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.DeliveryStatus
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.DeliveryStatus
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus
|
||||||
import org.apache.commons.codec.DecoderException
|
import org.apache.commons.codec.DecoderException
|
||||||
|
@ -27,7 +28,7 @@ class DefaultStatusResponseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* response (hex) 08202EAA0C0A1D1905281000004387D3039A
|
* response (hex) 1D1905281000004387D3039A
|
||||||
Status response: 29
|
Status response: 29
|
||||||
Pod status: RUNNING_BELOW_MIN_VOLUME
|
Pod status: RUNNING_BELOW_MIN_VOLUME
|
||||||
Basal active: true
|
Basal active: true
|
||||||
|
@ -66,7 +67,7 @@ class DefaultStatusResponseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* response (hex) 08202EAA080A1D180519C00E0039A7FF8085
|
* response (hex) 1D180519C00E0039A7FF8085
|
||||||
Status response: 29
|
Status response: 29
|
||||||
Pod status: RUNNING_ABOVE_MIN_VOLUME
|
Pod status: RUNNING_ABOVE_MIN_VOLUME
|
||||||
Basal active: true
|
Basal active: true
|
||||||
|
@ -103,4 +104,116 @@ class DefaultStatusResponseTest {
|
||||||
Assert.assertEquals(1023.toShort(), response.reservoirPulsesRemaining)
|
Assert.assertEquals(1023.toShort(), response.reservoirPulsesRemaining)
|
||||||
Assert.assertEquals(2611.toShort(), response.totalPulsesDelivered)
|
Assert.assertEquals(2611.toShort(), response.totalPulsesDelivered)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** response (hex) 1D990714201F0042ED8801DE
|
||||||
|
Status response: 29
|
||||||
|
Pod status: RUNNING_BELOW_MIN_VOLUME
|
||||||
|
Basal active: true
|
||||||
|
Temp Basal active: false
|
||||||
|
Immediate bolus active: false
|
||||||
|
Extended bolus active: true
|
||||||
|
Bolus pulses remaining: 31
|
||||||
|
sequence number of last programing command: 4
|
||||||
|
Total full pulses delivered: 3624
|
||||||
|
Full reservoir pulses remaining: 392
|
||||||
|
Time since activation: 4283
|
||||||
|
*/
|
||||||
|
@Test @Throws(DecoderException::class) fun testValidResponseReservoirPulsesRemaining() {
|
||||||
|
val encoded = Hex.decodeHex("1D990714201F0042ED8801DE")
|
||||||
|
val response = DefaultStatusResponse(encoded)
|
||||||
|
Assert.assertArrayEquals(encoded, response.encoded)
|
||||||
|
Assert.assertNotSame(encoded, response.encoded)
|
||||||
|
Assert.assertEquals(ResponseType.DEFAULT_STATUS_RESPONSE, response.responseType)
|
||||||
|
Assert.assertEquals(ResponseType.DEFAULT_STATUS_RESPONSE.value, response.messageType)
|
||||||
|
Assert.assertEquals(DeliveryStatus.UNKNOWN, response.deliveryStatus) // Extended bolus active
|
||||||
|
Assert.assertEquals(PodStatus.RUNNING_BELOW_MIN_VOLUME, response.podStatus)
|
||||||
|
Assert.assertEquals(4.toShort(), response.sequenceNumberOfLastProgrammingCommand)
|
||||||
|
Assert.assertEquals(31.toShort(), response.bolusPulsesRemaining)
|
||||||
|
Assert.assertEquals(0, response.activeAlerts.size)
|
||||||
|
Assert.assertEquals(4283.toShort(), response.minutesSinceActivation)
|
||||||
|
Assert.assertEquals(392.toShort(), response.reservoirPulsesRemaining)
|
||||||
|
Assert.assertEquals(3624.toShort(), response.totalPulsesDelivered)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** response (hex) 1d68002601f400002bff0368
|
||||||
|
Status response: 29
|
||||||
|
Pod status: RUNNING_BELOW_MIN_VOLUME
|
||||||
|
Basal active: true
|
||||||
|
Temp Basal active: false
|
||||||
|
Immediate bolus active: false
|
||||||
|
Extended bolus active: true
|
||||||
|
Bolus pulses remaining: 31
|
||||||
|
sequence number of last programing command: 4
|
||||||
|
Total full pulses delivered: 3624
|
||||||
|
Full reservoir pulses remaining: 392
|
||||||
|
Time since activation: 4283
|
||||||
|
*/
|
||||||
|
@Test @Throws(DecoderException::class) fun testValidResponseBolusPulsesRemaining3() {
|
||||||
|
val encoded = Hex.decodeHex("1d68002601f400002bff0368")
|
||||||
|
val response = DefaultStatusResponse(encoded)
|
||||||
|
Assert.assertArrayEquals(encoded, response.encoded)
|
||||||
|
Assert.assertNotSame(encoded, response.encoded)
|
||||||
|
Assert.assertEquals(500.toShort(), response.bolusPulsesRemaining)
|
||||||
|
Assert.assertEquals(0, response.activeAlerts.size)
|
||||||
|
}
|
||||||
|
/** response (hex) 1d28002e91e400002fff8256
|
||||||
|
Status response: 29
|
||||||
|
Pod status: RUNNING_BELOW_MIN_VOLUME
|
||||||
|
Basal active: true
|
||||||
|
Temp Basal active: false
|
||||||
|
Immediate bolus active: false
|
||||||
|
Extended bolus active: true
|
||||||
|
Bolus pulses remaining: 31
|
||||||
|
sequence number of last programing command: 4
|
||||||
|
Total full pulses delivered: 3624
|
||||||
|
Full reservoir pulses remaining: 392
|
||||||
|
Time since activation: 4283
|
||||||
|
*/
|
||||||
|
@Test @Throws(DecoderException::class) fun testValidResponseBolusPulsesRemaining4() {
|
||||||
|
val encoded = Hex.decodeHex("1d28002e91e400002fff8256")
|
||||||
|
val response = DefaultStatusResponse(encoded)
|
||||||
|
Assert.assertArrayEquals(encoded, response.encoded)
|
||||||
|
Assert.assertNotSame(encoded, response.encoded)
|
||||||
|
Assert.assertEquals(484.toShort(), response.bolusPulsesRemaining)
|
||||||
|
Assert.assertEquals(0, response.activeAlerts.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
1D980559C820404393FF83AA
|
||||||
|
Pod status: RUNNING_ABOVE_MIN_VOLUME
|
||||||
|
Basal active: true
|
||||||
|
Temp Basal active: false
|
||||||
|
Immediate bolus active: false
|
||||||
|
Extended bolus active: true
|
||||||
|
Bolus pulses remaining: 32
|
||||||
|
sequence number of last programing command: 9
|
||||||
|
Total full pulses delivered: 2739
|
||||||
|
Full reservoir pulses remaining: 1023
|
||||||
|
Time since activation: 4324
|
||||||
|
Alert 1 is InActive
|
||||||
|
Alert 2 is InActive
|
||||||
|
Alert 3 is InActive
|
||||||
|
Alert 4 is InActive
|
||||||
|
Alert 5 is InActive
|
||||||
|
Alert 6 is InActive
|
||||||
|
Alert 7 is Active
|
||||||
|
Occlusion alert active false
|
||||||
|
*/
|
||||||
|
@Test @Throws(DecoderException::class) fun testValidResponseActiveAlert1() {
|
||||||
|
val encoded = Hex.decodeHex("1D980559C820404393FF83AA")
|
||||||
|
val response = DefaultStatusResponse(encoded)
|
||||||
|
Assert.assertArrayEquals(encoded, response.encoded)
|
||||||
|
Assert.assertNotSame(encoded, response.encoded)
|
||||||
|
Assert.assertEquals(ResponseType.DEFAULT_STATUS_RESPONSE, response.responseType)
|
||||||
|
Assert.assertEquals(ResponseType.DEFAULT_STATUS_RESPONSE.value, response.messageType)
|
||||||
|
Assert.assertEquals(DeliveryStatus.UNKNOWN, response.deliveryStatus)
|
||||||
|
Assert.assertEquals(PodStatus.RUNNING_ABOVE_MIN_VOLUME, response.podStatus)
|
||||||
|
Assert.assertEquals(9.toShort(), response.sequenceNumberOfLastProgrammingCommand)
|
||||||
|
Assert.assertEquals(32.toShort(), response.bolusPulsesRemaining)
|
||||||
|
Assert.assertEquals(1, response.activeAlerts.size)
|
||||||
|
Assert.assertEquals(4324.toShort(), response.minutesSinceActivation)
|
||||||
|
Assert.assertEquals(1023.toShort(), response.reservoirPulsesRemaining)
|
||||||
|
Assert.assertEquals(2739.toShort(), response.totalPulsesDelivered)
|
||||||
|
Assert.assertEquals(true, response.activeAlerts.contains(AlertType.EXPIRATION))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue