diff --git a/build.gradle b/build.gradle
index cf4d289cfa..83722e0290 100644
--- a/build.gradle
+++ b/build.gradle
@@ -63,7 +63,7 @@ buildscript {
plugins {
id "io.gitlab.arturbosch.detekt" version "1.16.0-RC2"
- id "org.jlleitschuh.gradle.ktlint" version "9.4.1"
+ id "org.jlleitschuh.gradle.ktlint" version "10.1.0"
}
allprojects {
diff --git a/omnipod-common/src/main/res/values/strings.xml b/omnipod-common/src/main/res/values/strings.xml
index eb3ae323c6..040757704d 100644
--- a/omnipod-common/src/main/res/values/strings.xml
+++ b/omnipod-common/src/main/res/values/strings.xml
@@ -13,7 +13,11 @@
AAPS.Omnipod.low_reservoir_alert_units
AAPS.Omnipod.automatically_acknowledge_alerts_enabled
common_preferences_category_alerts
-
+ common_preferences_category_notifications_settings
+ AAPS.Omnipod.notification_uncertain_tbr_sound_enabled
+ AAPS.Omnipod.notification_uncertain_smb_sound_enabled
+ AAPS.Omnipod.notification_uncertain_bolus_sound_enabled
Pod Management
Actions
@@ -132,6 +136,11 @@
Other
Alerts
Confirmation Beeps
+ Notifications
+ Sound for uncertain TBR notifications enabled
+ Sound for
+ uncertain SMB notifications enabled
+ Sound for uncertain bolus notifications enabled
No Active Pod
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt
index 24370e0053..8ab1e04845 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt
@@ -7,7 +7,9 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.activities.ErrorHelperActivity.Companion.runAlarm
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.PumpEnactResult
+import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventProfileSwitchChanged
+import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.events.EventTempBasalChange
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
@@ -25,10 +27,7 @@ import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil
import info.nightscout.androidaps.plugins.pump.omnipod.common.definition.OmnipodCommandType
import info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command.*
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager
-import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ActivationProgress
-import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType
-import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.DeliveryStatus
-import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants
+import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.*
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.ResponseType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.CommandConfirmed
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
@@ -40,16 +39,24 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.OmnipodDashOvervi
import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.mapProfileToBasalProgram
import info.nightscout.androidaps.queue.commands.Command
import info.nightscout.androidaps.queue.commands.CustomCommand
+import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.TimeChangeType
import info.nightscout.androidaps.utils.resources.ResourceHelper
+import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.Completable
import io.reactivex.Single
+import io.reactivex.disposables.CompositeDisposable
+import io.reactivex.rxkotlin.plusAssign
import org.json.JSONObject
+import java.time.Duration
+import java.time.ZonedDateTime
import java.util.*
+import java.util.concurrent.CountDownLatch
import javax.inject.Inject
import javax.inject.Singleton
+import kotlin.concurrent.thread
import kotlin.math.ceil
import kotlin.random.Random
@@ -63,6 +70,8 @@ class OmnipodDashPumpPlugin @Inject constructor(
private val pumpSync: PumpSync,
private val rxBus: RxBusWrapper,
private val context: Context,
+ private val aapsSchedulers: AapsSchedulers,
+ private val fabricPrivacy: FabricPrivacy,
injector: HasAndroidInjector,
aapsLogger: AAPSLogger,
@@ -70,9 +79,13 @@ class OmnipodDashPumpPlugin @Inject constructor(
commandQueue: CommandQueueProvider
) : PumpPluginBase(pluginDescription, injector, aapsLogger, resourceHelper, commandQueue), Pump {
@Volatile var bolusCanceled = false
+ @Volatile var bolusDeliveryInProgress = false
+
private val handler: Handler = Handler(Looper.getMainLooper())
- lateinit private var statusChecker: Runnable
- var nextPodWarningCheck : Long = 0
+ private lateinit var statusChecker: Runnable
+ var nextPodWarningCheck: Long = 0
+ @Volatile var stopConnecting: CountDownLatch? = null
+ private var disposables: CompositeDisposable = CompositeDisposable()
companion object {
private const val BOLUS_RETRY_INTERVAL_MS = 2000.toLong()
@@ -95,10 +108,32 @@ class OmnipodDashPumpPlugin @Inject constructor(
statusChecker = Runnable {
refreshStatusOnUnacknowledgedCommands()
updatePodWarnings()
+ // createFakeTBRWhenNoActivePod()
+ // TODO: this is called from the main thread
handler.postDelayed(statusChecker, STATUS_CHECK_INTERVAL_MS)
}
}
+ private fun createFakeTBRWhenNoActivePod() {
+ if (!podStateManager.isPodRunning) {
+ val expectedState = pumpSync.expectedPumpState()
+ val tbr = expectedState.temporaryBasal
+ if (tbr == null || tbr.rate != 0.0) {
+ aapsLogger.info(LTag.PUMP, "createFakeTBRWhenNoActivePod")
+ pumpSync.syncTemporaryBasalWithPumpId(
+ timestamp = System.currentTimeMillis(),
+ rate = 0.0,
+ duration = T.mins(PodConstants.MAX_POD_LIFETIME.toMinutes()).msecs(),
+ isAbsolute = true,
+ type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
+ pumpId = Random.Default.nextLong(), // we don't use this, just make sure it's unique
+ pumpType = PumpType.OMNIPOD_DASH,
+ pumpSerial = serialNumber()
+ )
+ }
+ }
+ }
+
private fun updatePodWarnings() {
if (System.currentTimeMillis() > nextPodWarningCheck) {
if (!podStateManager.isPodRunning) {
@@ -121,7 +156,17 @@ class OmnipodDashPumpPlugin @Inject constructor(
rxBus.send(EventNewNotification(notification))
} else {
rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED))
- // TODO: time out of sync notification?
+ if (!podStateManager.sameTimeZone) {
+ val notification =
+ Notification(
+ Notification.OMNIPOD_TIME_OUT_OF_SYNC,
+ "Timezone on pod is different from the timezone on phone. " +
+ "Basal rate is incorrect" +
+ "Switch profile to fix",
+ Notification.NORMAL
+ )
+ rxBus.send(EventNewNotification(notification))
+ }
}
}
nextPodWarningCheck = DateTimeUtil.getTimeInFutureFromMinutes(15)
@@ -132,13 +177,13 @@ class OmnipodDashPumpPlugin @Inject constructor(
if (podStateManager.isPodRunning &&
podStateManager.activeCommand != null &&
commandQueue.size() == 0 &&
- commandQueue.performing() == null) {
+ commandQueue.performing() == null
+ ) {
commandQueue.readStatus("Unconfirmed command", null)
}
}
override fun isInitialized(): Boolean {
- // TODO
return true
}
@@ -153,37 +198,64 @@ class OmnipodDashPumpPlugin @Inject constructor(
override fun isConnected(): Boolean {
- return true
+ return !podStateManager.isPodRunning ||
+ podStateManager.bluetoothConnectionState == OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTED
}
override fun isConnecting(): Boolean {
- // TODO
- return false
+ return stopConnecting != null
}
override fun isHandshakeInProgress(): Boolean {
- // TODO
- return false
+ return stopConnecting != null &&
+ podStateManager.bluetoothConnectionState == OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTED
}
override fun finishHandshaking() {
- // TODO
}
override fun connect(reason: String) {
- // empty on purpose
+ aapsLogger.info(LTag.PUMP, "connect reason=$reason")
+ podStateManager.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTING
+
+ synchronized(this) {
+ stopConnecting?.let {
+ aapsLogger.warn(LTag.PUMP, "Already connecting: $stopConnecting")
+ return
+ }
+ val stop = CountDownLatch(1)
+ stopConnecting = stop
+ }
+
+ thread(
+ start = true,
+ name = "ConnectionThread",
+ ) {
+ try {
+ stopConnecting?.let {
+ val error = omnipodManager.connect(it).ignoreElements().blockingGet()
+ aapsLogger.info(LTag.PUMPCOMM, "connect error=$error")
+ }
+ } finally {
+ synchronized(this) {
+ stopConnecting = null
+ }
+ }
+ }
}
override fun disconnect(reason: String) {
- // TODO
+ aapsLogger.info(LTag.PUMP, "disconnect reason=$reason")
+ stopConnecting?.let { it.countDown() }
+ omnipodManager.disconnect(false)
}
override fun stopConnecting() {
- // TODO
+ aapsLogger.info(LTag.PUMP, "stopConnecting")
+ stopConnecting?.let { it.countDown() }
+ omnipodManager.disconnect(true)
}
-
-
override fun getPumpStatus(reason: String) {
aapsLogger.debug(LTag.PUMP, "getPumpStatus reason=$reason")
if (reason != "REQUESTED BY USER" && !podStateManager.isActivationCompleted) {
@@ -226,7 +298,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
pumpSync.syncTemporaryBasalWithPumpId(
timestamp = System.currentTimeMillis(),
rate = 0.0,
- duration = T.mins(PodConstants.MAX_POD_LIFETIME.standardMinutes).msecs(),
+ duration = T.mins(PodConstants.MAX_POD_LIFETIME.toMinutes()).msecs(),
isAbsolute = true,
type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
pumpId = Random.Default.nextLong(), // we don't use this, just make sure it's unique
@@ -279,8 +351,8 @@ class OmnipodDashPumpPlugin @Inject constructor(
podStateManager.createActiveCommand(historyId, basalProgram = basalProgram)
},
command = omnipodManager.setBasalProgram(basalProgram, hasBasalBeepEnabled()).ignoreElements(),
- post = failWhenUnconfirmed(deliverySuspended), // mark as failed even if it worked OK and try again vs. mark ok and
- // deny later
+ post = failWhenUnconfirmed(deliverySuspended),
+ // mark as failed even if it worked OK and try again vs. mark ok and deny later
).toPumpEnactResult()
}
@@ -305,6 +377,8 @@ class OmnipodDashPumpPlugin @Inject constructor(
}
Completable.error(java.lang.IllegalStateException("Command not confirmed"))
} else {
+ showNotification(Notification.PROFILE_SET_OK, "Profile set OK", Notification.INFO, null)
+
Completable.complete()
}
}
@@ -320,7 +394,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
.map {
pumpSyncTempBasal(
0.0,
- PodConstants.MAX_POD_LIFETIME.standardMinutes,
+ PodConstants.MAX_POD_LIFETIME.toMinutes(),
PumpSync.TemporaryBasalType.PUMP_SUSPEND
)
rxBus.send(EventTempBasalChange())
@@ -340,10 +414,38 @@ class OmnipodDashPumpPlugin @Inject constructor(
super.onStart()
podStateManager.onStart()
handler.postDelayed(statusChecker, STATUS_CHECK_INTERVAL_MS)
+ disposables += rxBus
+ .toObservable(EventPreferenceChange::class.java)
+ .observeOn(aapsSchedulers.main)
+ .subscribe(
+ {
+ if (it.isChanged(
+ resourceHelper,
+ R.string.key_omnipod_common_expiration_reminder_enabled
+ ) ||
+ it.isChanged(
+ resourceHelper,
+ R.string.key_omnipod_common_expiration_reminder_hours_before_shutdown
+ ) ||
+ it.isChanged(
+ resourceHelper,
+ R.string.key_omnipod_common_low_reservoir_alert_enabled
+ ) ||
+ it.isChanged(
+ resourceHelper,
+ R.string.key_omnipod_common_low_reservoir_alert_units
+ )
+ ) {
+ commandQueue.customCommand(CommandUpdateAlertConfiguration(), null)
+ }
+ },
+ fabricPrivacy::logException
+ )
}
override fun onStop() {
super.onStop()
+ disposables.clear()
handler.removeCallbacks(statusChecker)
}
@@ -363,7 +465,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
// TODO: what do we have to answer here if delivery is suspended?
val running = podStateManager.basalProgram
val equal = (mapProfileToBasalProgram(profile) == running)
- aapsLogger.info(LTag.PUMP, "isThisProfileSet: $equal")
+ aapsLogger.info(LTag.PUMP, "set: $equal. profile=$profile, running=$running")
return equal
}
@@ -400,7 +502,8 @@ class OmnipodDashPumpPlugin @Inject constructor(
override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult {
try {
- aapsLogger.info(LTag.PUMP, "Delivering treatment: $detailedBolusInfo")
+ bolusDeliveryInProgress = true
+ aapsLogger.info(LTag.PUMP, "Delivering treatment: $detailedBolusInfo $bolusCanceled")
val beepsConfigurationKey = if (detailedBolusInfo.bolusType == DetailedBolusInfo.BolusType.SMB)
R.string.key_omnipod_common_smb_beeps_enabled
else
@@ -469,9 +572,19 @@ class OmnipodDashPumpPlugin @Inject constructor(
)
} else {
if (podStateManager.activeCommand != null) {
+ val sound = if (sp.getBoolean(
+ R.string
+ .key_omnipod_common_notification_uncertain_bolus_sound_enabled,
+ true
+ )
+ )
+ R.raw.boluserror
+ else
+ 0
+
showErrorDialog(
"Bolus delivery status uncertain. Refresh pod status to confirm or deny.",
- R.raw.boluserror
+ sound
)
}
}
@@ -487,6 +600,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
return ret
} finally {
bolusCanceled = false
+ bolusDeliveryInProgress = false
}
}
@@ -621,7 +735,9 @@ class OmnipodDashPumpPlugin @Inject constructor(
override fun stopBolusDelivering() {
aapsLogger.info(LTag.PUMP, "stopBolusDelivering called")
- bolusCanceled = true
+ if (bolusDeliveryInProgress) {
+ bolusCanceled = true
+ }
}
override fun setTempBasalAbsolute(
@@ -910,7 +1026,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
.map {
pumpSyncTempBasal(
0.0,
- PodConstants.MAX_POD_LIFETIME.standardMinutes,
+ PodConstants.MAX_POD_LIFETIME.toMinutes(),
PumpSync.TemporaryBasalType.PUMP_SUSPEND
)
}
@@ -969,8 +1085,67 @@ class OmnipodDashPumpPlugin @Inject constructor(
}
private fun updateAlertConfiguration(): PumpEnactResult {
- // TODO
- return PumpEnactResult(injector).success(false).enacted(false).comment("NOT IMPLEMENTED")
+
+ val expirationReminderEnabled = sp.getBoolean(R.string.key_omnipod_common_expiration_reminder_enabled, true)
+ val expirationHours = sp.getInt(R.string.key_omnipod_common_expiration_reminder_hours_before_shutdown, 7)
+ val lowReservoirAlertEnabled = sp.getBoolean(R.string.key_omnipod_common_low_reservoir_alert_enabled, true)
+ val lowReservoirAlertUnits = sp.getInt(R.string.key_omnipod_common_low_reservoir_alert_units, 10)
+
+ if (!podStateManager.differentAlertSettings(
+ expirationReminderEnabled,
+ expirationHours,
+ lowReservoirAlertEnabled,
+ lowReservoirAlertUnits
+ )
+ ) {
+ return PumpEnactResult(injector).success(true).enacted(false)
+ }
+
+ val podLifeLeft = Duration.between(ZonedDateTime.now(), podStateManager.expiry)
+ val expiryAlertDelay = podLifeLeft.minus(Duration.ofHours(expirationHours.toLong()))
+ if (expiryAlertDelay.isNegative) {
+ aapsLogger.warn(
+ LTag.PUMPBTCOMM,
+ "updateAlertConfiguration negative " +
+ "expiryAlertDuration=$expiryAlertDelay"
+ )
+ PumpEnactResult(injector).success(false).enacted(false)
+ }
+ val alerts = listOf(
+ AlertConfiguration(
+ AlertType.LOW_RESERVOIR,
+ enabled = lowReservoirAlertEnabled,
+ durationInMinutes = 0,
+ autoOff = false,
+ AlertTrigger.ReservoirVolumeTrigger((lowReservoirAlertUnits * 10).toShort()),
+ BeepType.FOUR_TIMES_BIP_BEEP,
+ BeepRepetitionType.XXX
+ ),
+ AlertConfiguration(
+ AlertType.USER_SET_EXPIRATION,
+ enabled = expirationReminderEnabled,
+ durationInMinutes = 0,
+ autoOff = false,
+ AlertTrigger.TimerTrigger(
+ expiryAlertDelay.toMinutes().toShort()
+ ),
+ BeepType.FOUR_TIMES_BIP_BEEP,
+ BeepRepetitionType.XXX2
+ )
+ )
+ return executeProgrammingCommand(
+ historyEntry = history.createRecord(OmnipodCommandType.CONFIGURE_ALERTS),
+ command = omnipodManager.programAlerts(alerts).ignoreElements(),
+ post = podStateManager.updateExpirationAlertSettings(
+ expirationReminderEnabled,
+ expirationHours
+ ).andThen(
+ podStateManager.updateExpirationAlertSettings(
+ lowReservoirAlertEnabled,
+ lowReservoirAlertUnits
+ )
+ )
+ ).toPumpEnactResult()
}
private fun playTestBeep(): PumpEnactResult {
@@ -1029,11 +1204,17 @@ class OmnipodDashPumpPlugin @Inject constructor(
.map { handleCommandConfirmation(it) }
.ignoreElement(),
checkPodKaput(),
+ refreshOverview(),
post,
)
)
}
+ private fun refreshOverview(): Completable = Completable.defer {
+ rxBus.send(EventRefreshOverview("Dash command", false))
+ Completable.complete()
+ }
+
private fun handleCommandConfirmation(confirmation: CommandConfirmed) {
val command = confirmation.command
val historyEntry = history.getById(command.historyId)
@@ -1065,6 +1246,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED))
}
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS))
+ rxBus.send(EventDismissNotification(Notification.OMNIPOD_TIME_OUT_OF_SYNC))
}
OmnipodCommandType.SET_BASAL_PROFILE -> {
@@ -1086,6 +1268,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED))
rxBus.send(EventDismissNotification(Notification.FAILED_UPDATE_PROFILE))
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS))
+ rxBus.send(EventDismissNotification(Notification.OMNIPOD_TIME_OUT_OF_SYNC))
}
}
@@ -1186,14 +1369,22 @@ class OmnipodDashPumpPlugin @Inject constructor(
message,
urgency
)
- // TODO add back sound when we have options to disable it
- /*
- if (sound != null) {
+ if (sound != null && soundEnabledForNotificationType(id)) {
notification.soundId = sound
- }*/
+ }
rxBus.send(EventNewNotification(notification))
}
+ private fun soundEnabledForNotificationType(notificationType: Int): Boolean {
+ return when (notificationType) {
+ Notification.OMNIPOD_TBR_ALERTS ->
+ sp.getBoolean(R.string.key_omnipod_common_notification_uncertain_tbr_sound_enabled, true)
+ Notification.OMNIPOD_UNCERTAIN_SMB ->
+ sp.getBoolean(R.string.key_omnipod_common_notification_uncertain_smb_sound_enabled, true)
+ else -> true
+ }
+ }
+
private fun dismissNotification(id: Int) {
rxBus.send(EventDismissNotification(id))
}
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManager.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManager.kt
index 0a5b743f94..718df1597b 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManager.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManager.kt
@@ -9,12 +9,13 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definitio
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.ResponseType
import io.reactivex.Observable
import java.util.*
+import java.util.concurrent.CountDownLatch
interface OmnipodDashManager {
fun activatePodPart1(lowReservoirAlertTrigger: AlertTrigger.ReservoirVolumeTrigger?): Observable
- fun activatePodPart2(basalProgram: BasalProgram): Observable
+ fun activatePodPart2(basalProgram: BasalProgram, userConfiguredExpirationHours: Long?): Observable
fun getStatus(type: ResponseType.StatusResponseType): Observable
@@ -39,4 +40,8 @@ interface OmnipodDashManager {
fun silenceAlerts(alertTypes: EnumSet): Observable
fun deactivatePod(): Observable
+
+ fun disconnect(closeGatt: Boolean = false)
+
+ fun connect(stop: CountDownLatch): Observable
}
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt
index 487ecf2c6c..e504e93e62 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt
@@ -8,13 +8,17 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEven
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.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.response.*
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.Observable
import io.reactivex.functions.Action
import io.reactivex.functions.Consumer
+import java.time.Duration
+import java.time.ZonedDateTime
import java.util.*
+import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
@@ -74,6 +78,22 @@ class OmnipodDashManagerImpl @Inject constructor(
}
}
+ override fun disconnect(closeGatt: Boolean) {
+ bleManager.disconnect(closeGatt)
+ }
+
+ override fun connect(stop: CountDownLatch): Observable {
+ return observeConnectToPodWithStop(stop)
+ .interceptPodEvents()
+ }
+
+ private fun observeConnectToPodWithStop(stop: CountDownLatch): Observable {
+ return Observable.defer {
+ bleManager.connect(stop)
+ .doOnError { throwable -> logger.warn(LTag.PUMPBTCOMM, "observeConnectToPodWithStop error=$throwable") }
+ }
+ }
+
private val observeConnectToPod: Observable
get() = Observable.defer {
bleManager.connect()
@@ -221,10 +241,7 @@ class OmnipodDashManagerImpl @Inject constructor(
observeConnectToPod,
observeActivationPart1Commands(lowReservoirAlertTrigger)
).doOnComplete(ActivationProgressUpdater(ActivationProgress.PHASE_1_COMPLETED))
- // TODO these would be common for any observable returned in a public function in this class
- .doOnNext(PodEventInterceptor())
- .doOnError(ErrorInterceptor())
- .subscribeOn(aapsSchedulers.io)
+ .interceptPodEvents()
}
private fun observeActivationPart1Commands(lowReservoirAlertTrigger: AlertTrigger.ReservoirVolumeTrigger?): Observable {
@@ -320,20 +337,19 @@ class OmnipodDashManagerImpl @Inject constructor(
return observables.reversed()
}
- override fun activatePodPart2(basalProgram: BasalProgram): Observable {
+ override fun activatePodPart2(basalProgram: BasalProgram, userConfiguredExpirationHours: Long?):
+ Observable {
return Observable.concat(
observePodReadyForActivationPart2,
observeConnectToPod,
- observeActivationPart2Commands(basalProgram)
+ observeActivationPart2Commands(basalProgram, userConfiguredExpirationHours)
).doOnComplete(ActivationProgressUpdater(ActivationProgress.COMPLETED))
- // TODO these would be common for any observable returned in a public function in this class
- .doOnNext(PodEventInterceptor())
- .doOnError(ErrorInterceptor())
- .subscribeOn(aapsSchedulers.io)
+ .interceptPodEvents()
}
- private fun observeActivationPart2Commands(basalProgram: BasalProgram): Observable {
- val observables = createActivationPart2Observables(basalProgram)
+ private fun observeActivationPart2Commands(basalProgram: BasalProgram, userConfiguredExpirationHours: Long?):
+ Observable {
+ val observables = createActivationPart2Observables(basalProgram, userConfiguredExpirationHours)
return if (observables.isEmpty()) {
Observable.empty()
@@ -342,7 +358,11 @@ class OmnipodDashManagerImpl @Inject constructor(
}
}
- private fun createActivationPart2Observables(basalProgram: BasalProgram): List> {
+ private fun createActivationPart2Observables(
+ basalProgram: BasalProgram,
+ userConfiguredExpirationHours: Long?
+ ):
+ List> {
val observables = ArrayList>()
if (podStateManager.activationProgress.isBefore(ActivationProgress.CANNULA_INSERTED)) {
@@ -368,33 +388,60 @@ class OmnipodDashManagerImpl @Inject constructor(
)
}
if (podStateManager.activationProgress.isBefore(ActivationProgress.UPDATED_EXPIRATION_ALERTS)) {
+ val podLifeLeft = Duration.between(ZonedDateTime.now(), podStateManager.expiry)
+
+ val alerts = mutableListOf(
+ AlertConfiguration(
+ AlertType.EXPIRATION,
+ enabled = true,
+ durationInMinutes = TimeUnit.HOURS.toMinutes(7).toShort(),
+ autoOff = false,
+ AlertTrigger.TimerTrigger(
+ TimeUnit.HOURS.toMinutes(72).toShort()
+ ), // FIXME use activation time
+ BeepType.FOUR_TIMES_BIP_BEEP,
+ BeepRepetitionType.XXX3
+ ),
+ AlertConfiguration(
+ AlertType.EXPIRATION_IMMINENT,
+ enabled = true,
+ durationInMinutes = 0,
+ autoOff = false,
+ AlertTrigger.TimerTrigger(
+ TimeUnit.HOURS.toMinutes(79).toShort()
+ ), // FIXME use activation time
+ BeepType.FOUR_TIMES_BIP_BEEP,
+ BeepRepetitionType.XXX4
+ )
+ )
+ val userExpiryAlertDelay = podLifeLeft.minus(
+ Duration.ofHours(userConfiguredExpirationHours ?: MAX_POD_LIFETIME.toHours() + 1)
+ )
+ if (userExpiryAlertDelay.isNegative) {
+ logger.warn(
+ LTag.PUMPBTCOMM,
+ "createActivationPart2Observables negative " +
+ "expiryAlertDuration=$userExpiryAlertDelay"
+ )
+ } else {
+ alerts.add(
+ AlertConfiguration(
+ AlertType.USER_SET_EXPIRATION,
+ enabled = true,
+ durationInMinutes = 0,
+ autoOff = false,
+ AlertTrigger.TimerTrigger(
+ userExpiryAlertDelay.toMinutes().toShort()
+ ),
+ BeepType.FOUR_TIMES_BIP_BEEP,
+ BeepRepetitionType.XXX2
+ )
+ )
+ }
+
observables.add(
observeSendProgramAlertsCommand(
- listOf(
- // FIXME use user configured expiration alert
- AlertConfiguration(
- AlertType.EXPIRATION,
- enabled = true,
- durationInMinutes = TimeUnit.HOURS.toMinutes(7).toShort(),
- autoOff = false,
- AlertTrigger.TimerTrigger(
- TimeUnit.HOURS.toMinutes(73).toShort()
- ), // FIXME use activation time
- BeepType.FOUR_TIMES_BIP_BEEP,
- BeepRepetitionType.XXX3
- ),
- AlertConfiguration(
- AlertType.EXPIRATION_IMMINENT,
- enabled = true,
- durationInMinutes = TimeUnit.HOURS.toMinutes(1).toShort(),
- autoOff = false,
- AlertTrigger.TimerTrigger(
- TimeUnit.HOURS.toMinutes(79).toShort()
- ), // FIXME use activation time
- BeepType.FOUR_TIMES_BIP_BEEP,
- BeepRepetitionType.XXX4
- )
- ),
+ alerts,
multiCommandFlag = true
).doOnComplete(ActivationProgressUpdater(ActivationProgress.UPDATED_EXPIRATION_ALERTS))
)
@@ -414,11 +461,7 @@ class OmnipodDashManagerImpl @Inject constructor(
observeUniqueIdSet,
observeConnectToPod,
observeSendGetPodStatusCommand(type)
- )
- // TODO these would be common for any observable returned in a public function in this class
- .doOnNext(PodEventInterceptor())
- .doOnError(ErrorInterceptor())
- .subscribeOn(aapsSchedulers.io)
+ ).interceptPodEvents()
}
override fun setBasalProgram(basalProgram: BasalProgram, hasBasalBeepEnabled: Boolean): Observable {
@@ -426,11 +469,7 @@ class OmnipodDashManagerImpl @Inject constructor(
observePodRunning,
observeConnectToPod,
observeSendProgramBasalCommand(basalProgram, hasBasalBeepEnabled)
- )
- // TODO these would be common for any observable returned in a public function in this class
- .doOnNext(PodEventInterceptor())
- .doOnError(ErrorInterceptor())
- .subscribeOn(aapsSchedulers.io)
+ ).interceptPodEvents()
}
private fun observeSendStopDeliveryCommand(
@@ -461,11 +500,7 @@ class OmnipodDashManagerImpl @Inject constructor(
observePodRunning,
observeConnectToPod,
observeSendStopDeliveryCommand(StopDeliveryCommand.DeliveryType.ALL, hasBasalBeepEnabled)
- )
- // TODO these would be common for any observable returned in a public function in this class
- .doOnNext(PodEventInterceptor())
- .doOnError(ErrorInterceptor())
- .subscribeOn(aapsSchedulers.io)
+ ).interceptPodEvents()
}
override fun setTime(): Observable {
@@ -495,11 +530,7 @@ class OmnipodDashManagerImpl @Inject constructor(
observePodRunning,
observeConnectToPod,
observeSendProgramTempBasalCommand(rate, durationInMinutes, tempBasalBeeps)
- )
- // TODO these would be common for any observable returned in a public function in this class
- .doOnNext(PodEventInterceptor())
- .doOnError(ErrorInterceptor())
- .subscribeOn(aapsSchedulers.io)
+ ).interceptPodEvents()
}
override fun stopTempBasal(hasTempBasalBeepEnabled: Boolean): Observable {
@@ -507,11 +538,7 @@ class OmnipodDashManagerImpl @Inject constructor(
observePodRunning,
observeConnectToPod,
observeSendStopDeliveryCommand(StopDeliveryCommand.DeliveryType.TEMP_BASAL, hasTempBasalBeepEnabled)
- )
- // TODO these would be common for any observable returned in a public function in this class
- .doOnNext(PodEventInterceptor())
- .doOnError(ErrorInterceptor())
- .subscribeOn(aapsSchedulers.io)
+ ).interceptPodEvents()
}
override fun bolus(units: Double, confirmationBeeps: Boolean, completionBeeps: Boolean): Observable {
@@ -524,11 +551,7 @@ class OmnipodDashManagerImpl @Inject constructor(
confirmationBeeps,
completionBeeps
)
- )
- // TODO these would be common for any observable returned in a public function in this class
- .doOnNext(PodEventInterceptor())
- .doOnError(ErrorInterceptor())
- .subscribeOn(aapsSchedulers.io)
+ ).interceptPodEvents()
}
override fun stopBolus(beep: Boolean): Observable {
@@ -536,11 +559,7 @@ class OmnipodDashManagerImpl @Inject constructor(
observePodRunning,
observeConnectToPod,
observeSendStopDeliveryCommand(StopDeliveryCommand.DeliveryType.BOLUS, beep)
- )
- // TODO these would be common for any observable returned in a public function in this class
- .doOnNext(PodEventInterceptor())
- .doOnError(ErrorInterceptor())
- .subscribeOn(aapsSchedulers.io)
+ ).interceptPodEvents()
}
private fun observeSendConfigureBeepsCommand(
@@ -569,11 +588,7 @@ class OmnipodDashManagerImpl @Inject constructor(
observePodRunning,
observeConnectToPod,
observeSendConfigureBeepsCommand(immediateBeepType = beepType)
- )
- // TODO these would be common for any observable returned in a public function in this class
- .doOnNext(PodEventInterceptor())
- .doOnError(ErrorInterceptor())
- .subscribeOn(aapsSchedulers.io)
+ ).interceptPodEvents()
}
override fun programAlerts(alertConfigurations: List): Observable {
@@ -581,11 +596,7 @@ class OmnipodDashManagerImpl @Inject constructor(
observePodRunning,
observeConnectToPod,
observeSendProgramAlertsCommand(alertConfigurations)
- )
- // TODO these would be common for any observable returned in a public function in this class
- .doOnNext(PodEventInterceptor())
- .doOnError(ErrorInterceptor())
- .subscribeOn(aapsSchedulers.io)
+ ).interceptPodEvents()
}
private fun observeSendSilenceAlertsCommand(alertTypes: EnumSet): Observable {
@@ -607,11 +618,7 @@ class OmnipodDashManagerImpl @Inject constructor(
observePodRunning,
observeConnectToPod,
observeSendSilenceAlertsCommand(alertTypes)
- )
- // TODO these would be common for any observable returned in a public function in this class
- .doOnNext(PodEventInterceptor())
- .doOnError(ErrorInterceptor())
- .subscribeOn(aapsSchedulers.io)
+ ).interceptPodEvents()
}
private val observeSendDeactivateCommand: Observable
@@ -630,11 +637,7 @@ class OmnipodDashManagerImpl @Inject constructor(
return Observable.concat(
observeConnectToPod,
observeSendDeactivateCommand
- )
- // TODO these would be common for any observable returned in a public function in this class
- .doOnNext(PodEventInterceptor())
- .doOnError(ErrorInterceptor())
- .subscribeOn(aapsSchedulers.io)
+ ).interceptPodEvents()
//
.doOnComplete(podStateManager::reset)
}
@@ -711,6 +714,12 @@ class OmnipodDashManagerImpl @Inject constructor(
}
}
+ private fun Observable.interceptPodEvents(): Observable {
+ return this.doOnNext(PodEventInterceptor())
+ .doOnError(ErrorInterceptor())
+ .subscribeOn(aapsSchedulers.io)
+ }
+
inner class ErrorInterceptor : Consumer {
override fun accept(throwable: Throwable) {
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManager.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManager.kt
index b2400713c7..7a98a61b19 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManager.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManager.kt
@@ -1,10 +1,12 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm
+import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.Connection
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.ConnectionState
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.Response
import io.reactivex.Observable
+import java.util.concurrent.CountDownLatch
import kotlin.reflect.KClass
interface OmnipodDashBleManager {
@@ -13,9 +15,13 @@ interface OmnipodDashBleManager {
fun getStatus(): ConnectionState
- fun connect(): Observable
+ // used for sync connections
+ fun connect(timeoutMs: Long = Connection.BASE_CONNECT_TIMEOUT_MS * 3): Observable
+
+ // used for async connections
+ fun connect(stopConnectionLatch: CountDownLatch): Observable
fun pairNewPod(): Observable
- fun disconnect()
+ fun disconnect(closeGatt: Boolean = false)
}
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManagerImpl.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManagerImpl.kt
index 68a4f54960..60b9363238 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManagerImpl.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManagerImpl.kt
@@ -16,6 +16,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.b
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.Response
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
import io.reactivex.Observable
+import java.util.concurrent.CountDownLatch
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
import javax.inject.Singleton
@@ -83,7 +84,7 @@ class OmnipodDashBleManagerImpl @Inject constructor(
}
emitter.onComplete()
} catch (ex: Exception) {
- disconnect()
+ disconnect(false)
emitter.tryOnError(ex)
} finally {
busy.set(false)
@@ -100,56 +101,54 @@ class OmnipodDashBleManagerImpl @Inject constructor(
return connection?.let { it.connectionState() }
?: NotConnected
}
-
- override fun connect(): Observable = Observable.create { emitter ->
- if (!busy.compareAndSet(false, true)) {
- throw BusyException()
- }
- try {
- emitter.onNext(PodEvent.BluetoothConnecting)
-
- val podAddress =
- podState.bluetoothAddress
- ?: throw FailedToConnectException("Missing bluetoothAddress, activate the pod first")
- val podDevice = bluetoothAdapter.getRemoteDevice(podAddress)
- val conn = connection
- ?: Connection(podDevice, aapsLogger, context, podState)
- connection = conn
- if (conn.connectionState() is Connected && conn.session != null) {
- emitter.onNext(PodEvent.AlreadyConnected(podAddress))
- emitter.onComplete()
- return@create
- }
-
- // two retries
- for (i in 1..MAX_NUMBER_OF_CONNECTION_ATTEMPTS) {
- try {
- // wait i * CONNECTION_TIMEOUT
- conn.connect(CONNECT_TIMEOUT_MULTIPLIER)
- break
- } catch (e: Exception) {
- aapsLogger.warn(LTag.PUMPBTCOMM, "connect error=$e")
- if (i == MAX_NUMBER_OF_CONNECTION_ATTEMPTS) {
- emitter.onError(e)
- return@create
- }
- }
- }
-
- emitter.onNext(PodEvent.BluetoothConnected(podAddress))
- emitter.onNext(PodEvent.EstablishingSession)
- establishSession(1.toByte())
- emitter.onNext(PodEvent.Connected)
-
- emitter.onComplete()
- } catch (ex: Exception) {
- disconnect()
- emitter.tryOnError(ex)
- } finally {
- busy.set(false)
- }
+ // used for sync connections
+ override fun connect(timeoutMs: Long): Observable {
+ return connect(ConnectionWaitCondition(timeoutMs = timeoutMs))
}
+ // used for async connections
+ override fun connect(stopConnectionLatch: CountDownLatch): Observable {
+ return connect(ConnectionWaitCondition(stopConnection = stopConnectionLatch))
+ }
+
+ private fun connect(connectionWaitCond: ConnectionWaitCondition): Observable = Observable
+ .create {
+ emitter ->
+ if (!busy.compareAndSet(false, true)) {
+ throw BusyException()
+ }
+ try {
+ emitter.onNext(PodEvent.BluetoothConnecting)
+
+ val podAddress =
+ podState.bluetoothAddress
+ ?: throw FailedToConnectException("Missing bluetoothAddress, activate the pod first")
+ val podDevice = bluetoothAdapter.getRemoteDevice(podAddress)
+ val conn = connection
+ ?: Connection(podDevice, aapsLogger, context, podState)
+ connection = conn
+ if (conn.connectionState() is Connected && conn.session != null) {
+ emitter.onNext(PodEvent.AlreadyConnected(podAddress))
+ emitter.onComplete()
+ return@create
+ }
+
+ conn.connect(connectionWaitCond)
+
+ emitter.onNext(PodEvent.BluetoothConnected(podAddress))
+ emitter.onNext(PodEvent.EstablishingSession)
+ establishSession(1.toByte())
+ emitter.onNext(PodEvent.Connected)
+
+ emitter.onComplete()
+ } catch (ex: Exception) {
+ disconnect(false)
+ emitter.tryOnError(ex)
+ } finally {
+ busy.set(false)
+ }
+ }
+
private fun establishSession(msgSeq: Byte) {
val conn = assertConnected()
@@ -187,7 +186,6 @@ class OmnipodDashBleManagerImpl @Inject constructor(
throw BusyException()
}
try {
-
if (podState.ltk != null) {
emitter.onNext(PodEvent.AlreadyPaired)
emitter.onComplete()
@@ -207,12 +205,14 @@ class OmnipodDashBleManagerImpl @Inject constructor(
val podDevice = bluetoothAdapter.getRemoteDevice(podAddress)
val conn = Connection(podDevice, aapsLogger, context, podState)
connection = conn
+ conn.connect(ConnectionWaitCondition(timeoutMs = 3 * Connection.BASE_CONNECT_TIMEOUT_MS))
emitter.onNext(PodEvent.BluetoothConnected(podAddress))
emitter.onNext(PodEvent.Pairing)
+ val mIO = conn.msgIO ?: throw ConnectException("Connection lost")
val ltkExchanger = LTKExchanger(
aapsLogger,
- conn.msgIO,
+ mIO,
ids,
)
val pairResult = ltkExchanger.negotiateLTK()
@@ -221,27 +221,24 @@ class OmnipodDashBleManagerImpl @Inject constructor(
if (BuildConfig.DEBUG) {
aapsLogger.info(LTag.PUMPCOMM, "Got LTK: ${pairResult.ltk.toHex()}")
}
-
emitter.onNext(PodEvent.EstablishingSession)
establishSession(pairResult.msgSeq)
emitter.onNext(PodEvent.Connected)
emitter.onComplete()
} catch (ex: Exception) {
- disconnect()
+ disconnect(false)
emitter.tryOnError(ex)
} finally {
busy.set(false)
}
}
- override fun disconnect() {
- connection?.disconnect()
+ override fun disconnect(closeGatt: Boolean) {
+ connection?.disconnect(closeGatt)
?: aapsLogger.info(LTag.PUMPBTCOMM, "Trying to disconnect a null connection")
}
companion object {
- const val MAX_NUMBER_OF_CONNECTION_ATTEMPTS = 2
const val CONTROLLER_ID = 4242 // TODO read from preferences or somewhere else.
- private const val CONNECT_TIMEOUT_MULTIPLIER = 4
}
}
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/ServiceDiscoverer.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/ServiceDiscoverer.kt
index 664db805e0..2fc1c42704 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/ServiceDiscoverer.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/ServiceDiscoverer.kt
@@ -7,26 +7,40 @@ import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.BleCommCallbacks
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ConnectException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.CharacteristicType
+import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.Connected
+import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.Connection
+import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.Connection.Companion.STOP_CONNECTING_CHECK_INTERVAL_MS
+import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.ConnectionWaitCondition
import java.math.BigInteger
import java.util.*
class ServiceDiscoverer(
private val logger: AAPSLogger,
private val gatt: BluetoothGatt,
- private val bleCallbacks: BleCommCallbacks
+ private val bleCallbacks: BleCommCallbacks,
+ private val connection: Connection
) {
/***
* This is first step after connection establishment
*/
- fun discoverServices(): Map {
+ fun discoverServices(connectionWaitCond: ConnectionWaitCondition): Map {
logger.debug(LTag.PUMPBTCOMM, "Discovering services")
bleCallbacks.startServiceDiscovery()
val discover = gatt.discoverServices()
if (!discover) {
throw ConnectException("Could not start discovering services`")
}
- bleCallbacks.waitForServiceDiscovery(DISCOVER_SERVICES_TIMEOUT_MS)
+ connectionWaitCond.timeoutMs?.let {
+ bleCallbacks.waitForServiceDiscovery(it)
+ }
+ connectionWaitCond.stopConnection?.let {
+ while (!bleCallbacks.waitForServiceDiscovery(STOP_CONNECTING_CHECK_INTERVAL_MS)) {
+ if (it.count == 0L || connection.connectionState() !is Connected) {
+ throw ConnectException("stopConnecting called")
+ }
+ }
+ }
logger.debug(LTag.PUMPBTCOMM, "Services discovered")
val service = gatt.getService(SERVICE_UUID.toUuid())
?: throw ConnectException("Service not found: $SERVICE_UUID")
@@ -34,11 +48,10 @@ class ServiceDiscoverer(
?: throw ConnectException("Characteristic not found: ${CharacteristicType.CMD.value}")
val dataChar = service.getCharacteristic(CharacteristicType.DATA.uuid)
?: throw ConnectException("Characteristic not found: ${CharacteristicType.DATA.value}")
- var chars = mapOf(
+ return mapOf(
CharacteristicType.CMD to cmdChar,
CharacteristicType.DATA to dataChar
)
- return chars
}
private fun String.toUuid(): UUID = UUID(
@@ -49,6 +62,5 @@ class ServiceDiscoverer(
companion object {
private const val SERVICE_UUID = "1a7e-4024-e3ed-4464-8b7e-751e03d0dc5f"
- private const val DISCOVER_SERVICES_TIMEOUT_MS = 5000
}
}
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/callbacks/BleCommCallbacks.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/callbacks/BleCommCallbacks.kt
index 3d4d3d1339..6e193193b3 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/callbacks/BleCommCallbacks.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/callbacks/BleCommCallbacks.kt
@@ -40,7 +40,8 @@ class BleCommCallbacks(
if (newState == BluetoothProfile.STATE_CONNECTED && status == BluetoothGatt.GATT_SUCCESS) {
connected.countDown()
}
- if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+ if (newState == BluetoothProfile.STATE_DISCONNECTED && status != BluetoothGatt.GATT_SUCCESS) {
+ // If status == SUCCESS, it means that we initiated the disconnect.
disconnectHandler.onConnectionLost(status)
}
}
@@ -53,24 +54,28 @@ class BleCommCallbacks(
}
}
- fun waitForConnection(timeoutMs: Int) {
+ fun waitForConnection(timeoutMs: Long): Boolean {
+ val latch = connected
try {
- connected.await(timeoutMs.toLong(), TimeUnit.MILLISECONDS)
+ latch.await(timeoutMs, TimeUnit.MILLISECONDS)
} catch (e: InterruptedException) {
aapsLogger.warn(LTag.PUMPBTCOMM, "Interrupted while waiting for Connection")
}
+ return latch.count == 0L
}
fun startServiceDiscovery() {
serviceDiscoveryComplete = CountDownLatch(1)
}
- fun waitForServiceDiscovery(timeoutMs: Int) {
+ fun waitForServiceDiscovery(timeoutMs: Long): Boolean {
+ val latch = serviceDiscoveryComplete
try {
- serviceDiscoveryComplete.await(timeoutMs.toLong(), TimeUnit.MILLISECONDS)
+ latch.await(timeoutMs, TimeUnit.MILLISECONDS)
} catch (e: InterruptedException) {
aapsLogger.warn(LTag.PUMPBTCOMM, "Interrupted while waiting for ServiceDiscovery")
}
+ return latch.count == 0L
}
fun confirmWrite(expectedPayload: ByteArray, expectedUUID: String, timeoutMs: Long): WriteConfirmation {
@@ -206,6 +211,8 @@ class BleCommCallbacks(
fun resetConnection() {
aapsLogger.debug(LTag.PUMPBTCOMM, "Reset connection")
+ connected?.countDown()
+ serviceDiscoveryComplete?.countDown()
connected = CountDownLatch(1)
serviceDiscoveryComplete = CountDownLatch(1)
flushConfirmationQueue()
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/io/BleIO.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/io/BleIO.kt
index dbe1c24b88..a91d645339 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/io/BleIO.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/io/BleIO.kt
@@ -11,6 +11,7 @@ import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.BleCommCallbacks
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.WriteConfirmationError
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.WriteConfirmationSuccess
+import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command.BleCommandRTS
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.*
import java.util.concurrent.BlockingQueue
import java.util.concurrent.TimeUnit
@@ -84,12 +85,17 @@ open class BleIO(
* Called before sending a new message.
* The incoming queues should be empty, so we log when they are not.
*/
- fun flushIncomingQueue() {
+ open fun flushIncomingQueue(): Boolean {
+ var foundRTS = false
do {
val found = incomingPackets.poll()?.also {
- aapsLogger.warn(LTag.PUMPBTCOMM, "BleIO: queue not empty, flushing: {${it.toHex()}")
+ aapsLogger.warn(LTag.PUMPBTCOMM, "BleIO: queue not empty, flushing: ${it.toHex()}")
+ if (it.isNotEmpty() && it[0] == BleCommandRTS.data[0]) {
+ foundRTS = true
+ }
}
} while (found != null)
+ return foundRTS
}
/**
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/MessageIO.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/MessageIO.kt
index c18c2b33f1..0aa3033894 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/MessageIO.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/MessageIO.kt
@@ -35,7 +35,12 @@ class MessageIO(
@Suppress("ReturnCount")
fun sendMessage(msg: MessagePacket): MessageSendResult {
- cmdBleIO.flushIncomingQueue()
+ val foundRTS = cmdBleIO.flushIncomingQueue()
+ if (foundRTS) {
+ val msg = receiveMessage(false)
+ aapsLogger.warn(LTag.PUMPBTCOMM, "sendMessage received message=$msg")
+ throw IllegalStateException("Received message while trying to send")
+ }
dataBleIO.flushIncomingQueue()
val rtsSendResult = cmdBleIO.sendAndConfirmPacket(BleCommandRTS.data)
@@ -85,11 +90,13 @@ class MessageIO(
}
@Suppress("ReturnCount")
- fun receiveMessage(): MessagePacket? {
- val expectRTS = cmdBleIO.expectCommandType(BleCommandRTS, MESSAGE_READ_TIMEOUT_MS)
- if (expectRTS !is BleConfirmSuccess) {
- aapsLogger.warn(LTag.PUMPBTCOMM, "Error reading RTS: $expectRTS")
- return null
+ fun receiveMessage(readRTS: Boolean = true): MessagePacket? {
+ if (readRTS) {
+ val expectRTS = cmdBleIO.expectCommandType(BleCommandRTS, MESSAGE_READ_TIMEOUT_MS)
+ if (expectRTS !is BleConfirmSuccess) {
+ aapsLogger.warn(LTag.PUMPBTCOMM, "Error reading RTS: $expectRTS")
+ return null
+ }
}
val sendResult = cmdBleIO.sendAndConfirmPacket(BleCommandCTS.data)
@@ -219,6 +226,6 @@ class MessageIO(
companion object {
private const val MAX_PACKET_READ_TRIES = 4
- private const val MESSAGE_READ_TIMEOUT_MS = 2500.toLong()
+ private const val MESSAGE_READ_TIMEOUT_MS = 5000.toLong()
}
}
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Connection.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Connection.kt
index 98d4c9ff8c..261f42e0ea 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Connection.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Connection.kt
@@ -5,6 +5,7 @@ import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import android.content.Context
+import android.os.SystemClock
import info.nightscout.androidaps.extensions.toHex
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
@@ -13,105 +14,100 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Ids
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.ServiceDiscoverer
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.BleCommCallbacks
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.endecrypt.EnDecrypt
+import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ConnectException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.FailedToConnectException
-import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.BleSendSuccess
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.CharacteristicType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.CmdBleIO
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.DataBleIO
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.IncomingPackets
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageIO
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
+import java.lang.IllegalArgumentException
+import java.util.concurrent.CountDownLatch
sealed class ConnectionState
+object Connecting : ConnectionState()
object Connected : ConnectionState()
object NotConnected : ConnectionState()
+data class ConnectionWaitCondition(var timeoutMs: Long? = null, val stopConnection: CountDownLatch? = null) {
+ init {
+ if (timeoutMs == null && stopConnection == null) {
+ throw IllegalArgumentException("One of timeoutMs or stopConnection has to be non null")
+ }
+ if (timeoutMs != null && stopConnection != null) {
+ throw IllegalArgumentException("One of timeoutMs or stopConnection has to be null")
+ }
+ }
+}
+
class Connection(
private val podDevice: BluetoothDevice,
private val aapsLogger: AAPSLogger,
- context: Context,
+ private val context: Context,
private val podState: OmnipodDashPodStateManager
) : DisconnectHandler {
private val incomingPackets = IncomingPackets()
private val bleCommCallbacks = BleCommCallbacks(aapsLogger, incomingPackets, this)
- private val gattConnection: BluetoothGatt
+ private var gattConnection: BluetoothGatt? = null
private val bluetoothManager: BluetoothManager =
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
- // The session is Synchronized because we can lose the connection right when establishing it
+ @Volatile
var session: Session? = null
- @Synchronized get
- @Synchronized set
- private val cmdBleIO: CmdBleIO
- private val dataBleIO: DataBleIO
- init {
- aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to ${podDevice.address}")
+ @Volatile
+ var msgIO: MessageIO? = null
+ fun connect(connectionWaitCond: ConnectionWaitCondition) {
+ aapsLogger.debug("Connecting connectionWaitCond=$connectionWaitCond")
+
+ podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTING
val autoConnect = false
- podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTING
- gattConnection = podDevice.connectGatt(context, autoConnect, bleCommCallbacks, BluetoothDevice.TRANSPORT_LE)
- // OnDisconnect can be called after this point!!!
- val state = waitForConnection(2)
- if (state !is Connected) {
- podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.DISCONNECTED
- throw FailedToConnectException(podDevice.address)
- }
- podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTED
- val discoverer = ServiceDiscoverer(aapsLogger, gattConnection, bleCommCallbacks)
- val discoveredCharacteristics = discoverer.discoverServices()
- cmdBleIO = CmdBleIO(
- aapsLogger,
- discoveredCharacteristics[CharacteristicType.CMD]!!,
- incomingPackets
- .cmdQueue,
- gattConnection,
- bleCommCallbacks
- )
- dataBleIO = DataBleIO(
- aapsLogger,
- discoveredCharacteristics[CharacteristicType.DATA]!!,
- incomingPackets
- .dataQueue,
- gattConnection,
- bleCommCallbacks
- )
-
- val sendResult = cmdBleIO.hello()
- if (sendResult !is BleSendSuccess) {
- throw FailedToConnectException("Could not send HELLO command to ${podDevice.address}")
- }
- cmdBleIO.readyToRead()
- dataBleIO.readyToRead()
- }
-
- val msgIO = MessageIO(aapsLogger, cmdBleIO, dataBleIO)
-
- fun connect(timeoutMultiplier: Int) {
- if (session != null) {
- disconnect()
- }
- aapsLogger.debug("Connecting")
- podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTING
-
- if (!gattConnection.connect()) {
+ val gatt = gattConnection
+ ?: podDevice.connectGatt(context, autoConnect, bleCommCallbacks, BluetoothDevice.TRANSPORT_LE)
+ gattConnection = gatt
+ if (!gatt.connect()) {
throw FailedToConnectException("connect() returned false")
}
-
- if (waitForConnection(timeoutMultiplier) !is Connected) {
+ val before = SystemClock.elapsedRealtime()
+ if (waitForConnection(connectionWaitCond) !is Connected) {
podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.DISCONNECTED
throw FailedToConnectException(podDevice.address)
}
+ val waitedMs = SystemClock.elapsedRealtime() - before
+ val timeoutMs = connectionWaitCond.timeoutMs
+ if (timeoutMs != null) {
+ var newTimeout = timeoutMs - waitedMs
+ if (newTimeout < MIN_DISCOVERY_TIMEOUT_MS) {
+ newTimeout = MIN_DISCOVERY_TIMEOUT_MS
+ }
+ connectionWaitCond.timeoutMs = newTimeout
+ }
podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTED
- val discoverer = ServiceDiscoverer(aapsLogger, gattConnection, bleCommCallbacks)
- val discovered = discoverer.discoverServices()
- dataBleIO.characteristic = discovered[CharacteristicType.DATA]!!
- cmdBleIO.characteristic = discovered[CharacteristicType.CMD]!!
-
+ val discoverer = ServiceDiscoverer(aapsLogger, gatt, bleCommCallbacks, this)
+ val discovered = discoverer.discoverServices(connectionWaitCond)
+ val cmdBleIO = CmdBleIO(
+ aapsLogger,
+ discovered[CharacteristicType.CMD]!!,
+ incomingPackets
+ .cmdQueue,
+ gatt,
+ bleCommCallbacks
+ )
+ val dataBleIO = DataBleIO(
+ aapsLogger,
+ discovered[CharacteristicType.DATA]!!,
+ incomingPackets
+ .dataQueue,
+ gatt,
+ bleCommCallbacks
+ )
+ msgIO = MessageIO(aapsLogger, cmdBleIO, dataBleIO)
// val ret = gattConnection.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH)
// aapsLogger.info(LTag.PUMPBTCOMM, "requestConnectionPriority: $ret")
cmdBleIO.hello()
@@ -119,18 +115,33 @@ class Connection(
dataBleIO.readyToRead()
}
- fun disconnect() {
- aapsLogger.debug(LTag.PUMPBTCOMM, "Disconnecting")
+ fun disconnect(closeGatt: Boolean) {
+ aapsLogger.debug(LTag.PUMPBTCOMM, "Disconnecting closeGatt=$closeGatt")
podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.DISCONNECTED
-
- gattConnection.disconnect()
+ if (closeGatt) {
+ gattConnection?.close()
+ gattConnection = null
+ } else {
+ gattConnection?.disconnect()
+ }
bleCommCallbacks.resetConnection()
session = null
+ msgIO = null
}
- private fun waitForConnection(timeoutMultiplier: Int): ConnectionState {
+ private fun waitForConnection(connectionWaitCond: ConnectionWaitCondition): ConnectionState {
+ aapsLogger.debug(LTag.PUMPBTCOMM, "waitForConnection connectionWaitCond=$connectionWaitCond")
try {
- bleCommCallbacks.waitForConnection(BASE_CONNECT_TIMEOUT_MS * timeoutMultiplier)
+ connectionWaitCond.timeoutMs?.let {
+ bleCommCallbacks.waitForConnection(it)
+ }
+ connectionWaitCond.stopConnection?.let {
+ while (!bleCommCallbacks.waitForConnection(STOP_CONNECTING_CHECK_INTERVAL_MS)) {
+ if (it.count == 0L) {
+ throw ConnectException("stopConnecting called")
+ }
+ }
+ }
} catch (e: InterruptedException) {
// We are still going to check if connection was successful
aapsLogger.info(LTag.PUMPBTCOMM, "Interrupted while waiting for connection")
@@ -148,7 +159,9 @@ class Connection(
}
fun establishSession(ltk: ByteArray, msgSeq: Byte, ids: Ids, eapSqn: ByteArray): EapSqn? {
- val eapAkaExchanger = SessionEstablisher(aapsLogger, msgIO, ltk, eapSqn, ids, msgSeq)
+ val mIO = msgIO ?: throw ConnectException("Connection lost")
+
+ val eapAkaExchanger = SessionEstablisher(aapsLogger, mIO, ltk, eapSqn, ids, msgSeq)
return when (val keys = eapAkaExchanger.negotiateSessionKeys()) {
is SessionNegotiationResynchronization -> {
if (BuildConfig.DEBUG) {
@@ -168,7 +181,7 @@ class Connection(
keys.nonce,
keys.ck
)
- session = Session(aapsLogger, msgIO, ids, sessionKeys = keys, enDecrypt = enDecrypt)
+ session = Session(aapsLogger, mIO, ids, sessionKeys = keys, enDecrypt = enDecrypt)
null
}
}
@@ -177,10 +190,12 @@ class Connection(
// This will be called from a different thread !!!
override fun onConnectionLost(status: Int) {
aapsLogger.info(LTag.PUMPBTCOMM, "Lost connection with status: $status")
- disconnect()
+ disconnect(false)
}
companion object {
- private const val BASE_CONNECT_TIMEOUT_MS = 10000
+ const val BASE_CONNECT_TIMEOUT_MS = 10000L
+ const val MIN_DISCOVERY_TIMEOUT_MS = 10000L
+ const val STOP_CONNECTING_CHECK_INTERVAL_MS = 500L
}
}
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/EapAkaAttribute.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/EapAkaAttribute.kt
index 94379b181c..b81d5c1216 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/EapAkaAttribute.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/EapAkaAttribute.kt
@@ -1,7 +1,6 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session
import info.nightscout.androidaps.extensions.toHex
-import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.MessageIOException
import java.util.*
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Session.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Session.kt
index 87fccc725e..4c4531689a 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Session.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Session.kt
@@ -11,7 +11,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.StringLengthPrefixEncoding.Companion.parseKeys
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.Response
-import kotlin.reflect.KClass
sealed class CommandSendResult
object CommandSendSuccess : CommandSendResult()
@@ -113,10 +112,10 @@ class Session(
// TODO verify length
- //val uniqueId = data.copyOfRange(0, 4)
- //val lenghtAndSequenceNumber = data.copyOfRange(4, 6)
+ // val uniqueId = data.copyOfRange(0, 4)
+ // val lenghtAndSequenceNumber = data.copyOfRange(4, 6)
val payload = data.copyOfRange(6, data.size - 2)
- //val crc = data.copyOfRange(data.size - 2, data.size)
+ // val crc = data.copyOfRange(data.size - 2, data.size)
// TODO validate uniqueId, sequenceNumber and crc
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/BeepRepetitionType.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/BeepRepetitionType.kt
index 0668852456..d7a1a8f761 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/BeepRepetitionType.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/BeepRepetitionType.kt
@@ -5,9 +5,9 @@ enum class BeepRepetitionType(
val value: Byte
) {
- XXX(0x01.toByte()), // Used in lump of coal alert
- XXX2(0x03.toByte()), // Used in low reservoir alert
- XXX3(0x05.toByte()), // Used in user pod expiration alert
- XXX4(0x06.toByte()), // Used in pod expiration alert
- XXX5(0x08.toByte()); // Used in imminent pod expiration alert
+ XXX(0x01.toByte()), // Used in lump of coal alert, LOW_RESERVOIR
+ XXX2(0x03.toByte()), // Used in USER_SET_EXPIRATION
+ XXX3(0x05.toByte()), // published system expiration alert
+ XXX4(0x06.toByte()), // Used in imminent pod expiration alert
+ XXX5(0x08.toByte()); // Lump of coal alert
}
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/PodConstants.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/PodConstants.kt
index f52cc8fb5f..ae648dd656 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/PodConstants.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/PodConstants.kt
@@ -1,9 +1,9 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
-import org.joda.time.Duration
+import java.time.Duration
class PodConstants {
companion object {
- val MAX_POD_LIFETIME = Duration.standardHours(80)
+ val MAX_POD_LIFETIME = Duration.ofMinutes(80)
}
}
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManager.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManager.kt
index ebf81c8de9..af833c4552 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManager.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManager.kt
@@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.pair.PairResult
-import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.*
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.AlarmStatusResponse
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.DefaultStatusResponse
@@ -11,12 +10,9 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse
import io.reactivex.Completable
import io.reactivex.Maybe
-import io.reactivex.Observable
import io.reactivex.Single
-import org.joda.time.DateTime
-import org.joda.time.DateTimeZone
-import org.joda.time.Duration
import java.io.Serializable
+import java.time.ZonedDateTime
import java.util.*
sealed class CommandConfirmationFromState
@@ -37,11 +33,12 @@ interface OmnipodDashPodStateManager {
var bluetoothConnectionState: BluetoothConnectionState
var timeZone: TimeZone
+ val sameTimeZone: Boolean // The TimeZone is the same on the phone and on the pod
val lastUpdatedSystem: Long // System.currentTimeMillis()
val lastStatusResponseReceived: Long
- val time: DateTime?
- val timeDrift: Duration?
- val expiry: DateTime?
+ val time: ZonedDateTime?
+ val timeDrift: java.time.Duration?
+ val expiry: ZonedDateTime?
val messageSequenceNumber: Short
val sequenceNumberOfLastProgrammingCommand: Short?
@@ -104,6 +101,9 @@ interface OmnipodDashPodStateManager {
- after getPodStatus was successful(we have an up-to-date podStatus)
*/
fun recoverActivationFromPodStatus(): String?
+ fun differentAlertSettings(expirationReminderEnabled: Boolean, expirationHours: Int, lowReservoirAlertEnabled: Boolean, lowReservoirAlertUnits: Int): Boolean
+ fun updateExpirationAlertSettings(expirationReminderEnabled: Boolean, expirationHours: Int): Completable
+ fun updateLowReservoirAlertSettings(lowReservoirAlertEnabled: Boolean, lowReservoirAlertUnits: Int): Completable
data class ActiveCommand(
val sequence: Short,
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt
index 2656a6149c..ad9d789467 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt
@@ -11,7 +11,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.R
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.pair.PairResult
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.EapSqn
-import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.*
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.AlarmStatusResponse
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.DefaultStatusResponse
@@ -20,12 +19,11 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.Completable
import io.reactivex.Maybe
-import io.reactivex.Observable
import io.reactivex.Single
-import org.joda.time.DateTime
-import org.joda.time.DateTimeZone
-import org.joda.time.Duration
import java.io.Serializable
+import java.time.Duration
+import java.time.Instant
+import java.time.ZonedDateTime
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
@@ -107,6 +105,12 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
store()
}
+ override val sameTimeZone: Boolean
+ get() {
+ val now = System.currentTimeMillis()
+ return TimeZone.getDefault().getOffset(now) == timeZone.getOffset(now)
+ }
+
override val bluetoothVersion: SoftwareVersion?
get() = podState.bleVersion
@@ -183,36 +187,40 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
override val lastStatusResponseReceived: Long
get() = podState.lastStatusResponseReceived
- override val time: DateTime?
+ override val time: ZonedDateTime?
get() {
val minutesSinceActivation = podState.minutesSinceActivation
val activationTime = podState.activationTime
if ((activationTime != null) && (minutesSinceActivation != null)) {
- return DateTime(activationTime)
- .plusMinutes(minutesSinceActivation.toInt())
- .plus(Duration(podState.lastUpdatedSystem, System.currentTimeMillis()))
+ return ZonedDateTime.ofInstant(Instant.ofEpochMilli(activationTime), timeZone.toZoneId())
+ .plusMinutes(minutesSinceActivation.toLong())
+ .plus(Duration.ofMillis(System.currentTimeMillis() - lastUpdatedSystem))
}
return null
}
override val timeDrift: Duration?
get() {
- return Duration(DateTime.now(), time)
+ return Duration.between(ZonedDateTime.now(), time)
}
- override val expiry: DateTime?
- // TODO: Consider storing expiry datetime in pod state saving continuously recalculating to the same value
+ override val expiry: ZonedDateTime?
get() {
val podLifeInHours = podLifeInHours
- val activationTime = podState.activationTime
- if (podLifeInHours != null && activationTime != null) {
- return DateTime(podState.activationTime).plusHours(podLifeInHours.toInt())
+ val minutesSinceActivation = podState.minutesSinceActivation
+ if (podLifeInHours != null && minutesSinceActivation != null) {
+ return ZonedDateTime.now()
+ .plusHours(podLifeInHours.toLong())
+ .minusMinutes(minutesSinceActivation.toLong())
+ .plus(Duration.ofMillis(System.currentTimeMillis() - lastUpdatedSystem))
}
return null
}
override var bluetoothConnectionState: OmnipodDashPodStateManager.BluetoothConnectionState
+ @Synchronized
get() = podState.bluetoothConnectionState
+ @Synchronized
set(bluetoothConnectionState) {
podState.bluetoothConnectionState = bluetoothConnectionState
rxBus.send(EventOmnipodDashPumpValuesChanged())
@@ -283,29 +291,29 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
requestedBolus: Double?
):
Single {
- return Single.create { source ->
- if (activeCommand == null) {
- val command = OmnipodDashPodStateManager.ActiveCommand(
- podState.messageSequenceNumber,
- createdRealtime = SystemClock.elapsedRealtime(),
- historyId = historyId,
- sendError = null,
- basalProgram = basalProgram,
- tempBasal = tempBasal,
- requestedBolus = requestedBolus
+ return Single.create { source ->
+ if (activeCommand == null) {
+ val command = OmnipodDashPodStateManager.ActiveCommand(
+ podState.messageSequenceNumber,
+ createdRealtime = SystemClock.elapsedRealtime(),
+ historyId = historyId,
+ sendError = null,
+ basalProgram = basalProgram,
+ tempBasal = tempBasal,
+ requestedBolus = requestedBolus
+ )
+ podState.activeCommand = command
+ source.onSuccess(command)
+ } else {
+ source.onError(
+ java.lang.IllegalStateException(
+ "Trying to send a command " +
+ "and the last command was not confirmed"
)
- podState.activeCommand = command
- source.onSuccess(command)
- } else {
- source.onError(
- java.lang.IllegalStateException(
- "Trying to send a command " +
- "and the last command was not confirmed"
- )
- )
- }
+ )
}
}
+ }
@Synchronized
override fun observeNoActiveCommand(): Completable {
@@ -391,6 +399,32 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
}
}
+ override fun differentAlertSettings(
+ expirationReminderEnabled: Boolean,
+ expirationHours: Int,
+ lowReservoirAlertEnabled: Boolean,
+ lowReservoirAlertUnits: Int
+ ): Boolean {
+ return podState.expirationReminderEnabled == expirationReminderEnabled &&
+ podState.expirationHours == expirationHours &&
+ podState.lowReservoirAlertEnabled == lowReservoirAlertEnabled &&
+ podState.lowReservoirAlertUnits == lowReservoirAlertUnits
+ }
+
+ override fun updateExpirationAlertSettings(expirationReminderEnabled: Boolean, expirationHours: Int):
+ Completable = Completable.defer {
+ podState.expirationReminderEnabled = expirationReminderEnabled
+ podState.expirationHours = expirationHours
+ Completable.complete()
+ }
+
+ override fun updateLowReservoirAlertSettings(lowReservoirAlertEnabled: Boolean, lowReservoirAlertUnits: Int):
+ Completable = Completable.defer {
+ podState.lowReservoirAlertEnabled = lowReservoirAlertEnabled
+ podState.lowReservoirAlertUnits = lowReservoirAlertUnits
+ Completable.complete()
+ }
+
@Synchronized
override fun getCommandConfirmationFromState(): CommandConfirmationFromState {
return podState.activeCommand?.run {
@@ -429,7 +463,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
override fun onStart() {
when (getCommandConfirmationFromState()) {
CommandConfirmationSuccess, CommandConfirmationDenied -> {
- val now = System.currentTimeMillis()
+ val now = SystemClock.elapsedRealtime()
val newCommand = podState.activeCommand?.copy(
createdRealtime = now,
sentRealtime = now + 1
@@ -439,7 +473,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
}
CommandSendingNotConfirmed -> {
- val now = System.currentTimeMillis()
+ val now = SystemClock.elapsedRealtime()
val newCommand = podState.activeCommand?.copy(
createdRealtime = now,
sentRealtime = now + 1
@@ -611,6 +645,11 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
var firstPrimeBolusVolume: Short? = null
var secondPrimeBolusVolume: Short? = null
+ var expirationReminderEnabled: Boolean? = null
+ var expirationHours: Int? = null
+ var lowReservoirAlertEnabled: Boolean? = null
+ var lowReservoirAlertUnits: Int? = null
+
var pulsesDelivered: Short? = null
var pulsesRemaining: Short? = null
var podStatus: PodStatus? = null
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt
index 59e90ef7dc..97f70338a5 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt
@@ -44,8 +44,8 @@ import info.nightscout.androidaps.utils.ui.UIRunnable
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import org.apache.commons.lang3.StringUtils
-import org.joda.time.DateTime
-import org.joda.time.Duration
+import java.time.Duration
+import java.time.ZonedDateTime
import java.util.*
import javax.inject.Inject
import kotlin.collections.ArrayList
@@ -269,34 +269,41 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
// Update time on Pod
podInfoBinding.timeOnPod.text = podStateManager.time?.let {
- readableZonedTime(it)
+ resourceHelper.gs(
+ R.string.omnipod_common_time_with_timezone,
+ dateUtil.dateAndTimeString(it.toEpochSecond() * 1000),
+ podStateManager.timeZone.getDisplayName(true, TimeZone.SHORT)
+ )
} ?: PLACEHOLDER
+ val timeDeviationTooBig = podStateManager.timeDrift?.let {
+ Duration.ofMinutes(MAX_TIME_DEVIATION_MINUTES).minus(
+ it.abs()
+ ).isNegative
+ } ?: false
podInfoBinding.timeOnPod.setTextColor(
- podStateManager.timeDrift?.let {
- if (it.abs().isLongerThan(Duration.standardMinutes(MAX_TIME_DEVIATION_MINUTES))) {
- Color.RED
- } else {
+ when {
+ !podStateManager.sameTimeZone ->
+ Color.MAGENTA
+ timeDeviationTooBig ->
+ Color.YELLOW
+ else ->
Color.WHITE
- }
- } ?: Color.WHITE
+ }
)
// Update Pod expiry time
val expiresAt = podStateManager.expiry
- if (expiresAt == null) {
- podInfoBinding.podExpiryDate.text = PLACEHOLDER
- podInfoBinding.podExpiryDate.setTextColor(Color.WHITE)
- } else {
- podInfoBinding.podExpiryDate.text = readableZonedTime(expiresAt)
- podInfoBinding.podExpiryDate.setTextColor(
- if (DateTime.now().isAfter(expiresAt)) {
- Color.RED
- } else {
- Color.WHITE
- }
- )
+ podInfoBinding.podExpiryDate.text = expiresAt?.let {
+ dateUtil.dateAndTimeString(it.toEpochSecond() * 1000)
}
+ ?: PLACEHOLDER
+ podInfoBinding.podExpiryDate.setTextColor(
+ if (expiresAt != null && ZonedDateTime.now().isAfter(expiresAt))
+ Color.RED
+ else
+ Color.WHITE
+ )
podStateManager.alarmType?.let {
errors.add(
@@ -371,14 +378,14 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
private fun updateLastConnection() {
if (podStateManager.isUniqueIdSet) {
podInfoBinding.lastConnection.text = readableDuration(
- Duration(
- podStateManager.lastUpdatedSystem,
- System
- .currentTimeMillis()
+ Duration.ofMillis(
+ System.currentTimeMillis() -
+ podStateManager.lastUpdatedSystem,
+
)
)
val lastConnectionColor =
- if (omnipodDashPumpPlugin.isUnreachableAlertTimeoutExceeded(getPumpUnreachableTimeout().millis)) {
+ if (omnipodDashPumpPlugin.isUnreachableAlertTimeoutExceeded(getPumpUnreachableTimeout().toMillis())) {
Color.RED
} else {
Color.WHITE
@@ -425,12 +432,14 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
}
}
- val podStatusColor =
- if (!podStateManager.isActivationCompleted || podStateManager.isPodKaput || podStateManager.isSuspended) {
+ val podStatusColor = when {
+ !podStateManager.isActivationCompleted || podStateManager.isPodKaput || podStateManager.isSuspended ->
Color.RED
- } else {
+ podStateManager.activeCommand != null ->
+ Color.YELLOW
+ else ->
Color.WHITE
- }
+ }
podInfoBinding.podStatus.setTextColor(podStatusColor)
}
@@ -444,7 +453,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
R.string.omnipod_common_overview_last_bolus_value,
omnipodDashPumpPlugin.model().determineCorrectBolusSize(requestedBolus),
resourceHelper.gs(R.string.insulin_unit_shortname),
- readableDuration(Duration(it.createdRealtime, SystemClock.elapsedRealtime()))
+ readableDuration(Duration.ofMillis(SystemClock.elapsedRealtime() - it.createdRealtime))
)
text += " (uncertain) "
textColor = Color.RED
@@ -464,7 +473,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
R.string.omnipod_common_overview_last_bolus_value,
omnipodDashPumpPlugin.model().determineCorrectBolusSize(bolusSize),
resourceHelper.gs(R.string.insulin_unit_shortname),
- readableDuration(Duration(it.startTime, System.currentTimeMillis()))
+ readableDuration(Duration.ofMillis(System.currentTimeMillis() - it.startTime))
)
if (!it.deliveryComplete) {
textColor = Color.YELLOW
@@ -600,47 +609,10 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
}
}
- // private fun getTimeZone(): DateTimeZone {
- // // return getSafe(() -> podState.getTimeZone());
- // return podStateManager.timeZone
- // }
- private fun getTimeZone(): TimeZone {
- // Return timezone ID (e.g "Europe/Amsterdam")
- return podStateManager.timeZone
- }
-
- private fun readableZonedTime(time: DateTime): String {
- val timeAsJavaData = time.toLocalDateTime().toDate()
- return dateUtil.dateAndTimeString(timeAsJavaData.time)
-
- // // TODO: Handle timeZone ID
- // val timeZone = getTimeZone()
- // if (timeZone == "") {
- // // No timezone defined, use local time (default)
- // return dateUtil.dateAndTimeString(timeAsJavaData.time)
- // }
- // else {
- // // Get full timezoned time
- // val isDaylightTime = timeZone.inDaylightTime(timeAsJavaData)
- // val locale = resources.configuration.locales.get(0)
- // val timeZoneDisplayName =
- // timeZone.getDisplayName(isDaylightTime, TimeZone.SHORT, locale) + " " + timeZone.getDisplayName(
- // isDaylightTime,
- // TimeZone.LONG,
- // locale
- // )
- // return resourceHelper.gs(
- // R.string.omnipod_common_time_with_timezone,
- // dateUtil.dateAndTimeString(timeAsJavaData.time),
- // timeZoneDisplayName
- // )
- // }
- }
-
private fun readableDuration(duration: Duration): String {
- val hours = duration.standardHours.toInt()
- val minutes = duration.standardMinutes.toInt()
- val seconds = duration.standardSeconds.toInt()
+ val hours = duration.toHours().toInt()
+ val minutes = duration.toMinutes().toInt()
+ val seconds = duration.seconds
when {
seconds < 10 -> {
return resourceHelper.gs(R.string.omnipod_common_moments_ago)
@@ -683,7 +655,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
// FIXME ideally we should just have access to LocalAlertUtils here
private fun getPumpUnreachableTimeout(): Duration {
- return Duration.standardMinutes(
+ return Duration.ofMinutes(
sp.getInt(
R.string.key_pump_unreachable_threshold_minutes,
Constants.DEFAULT_PUMP_UNREACHABLE_THRESHOLD_MINUTES
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInitializePodViewModel.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInitializePodViewModel.kt
index 1a410acd49..4862dc6a46 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInitializePodViewModel.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInitializePodViewModel.kt
@@ -9,6 +9,8 @@ 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.driver.OmnipodDashManager
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertTrigger
+import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
+import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.Single
import io.reactivex.rxkotlin.subscribeBy
import javax.inject.Inject
@@ -16,9 +18,10 @@ import javax.inject.Inject
class DashInitializePodViewModel @Inject constructor(
private val omnipodManager: OmnipodDashManager,
injector: HasAndroidInjector,
- logger: AAPSLogger
+ logger: AAPSLogger,
+ private val sp: SP,
+ private val podStateManager: OmnipodDashPodStateManager,
) : InitializePodViewModel(injector, logger) {
-
override fun isPodInAlarm(): Boolean = false // TODO
override fun isPodActivationTimeExceeded(): Boolean = false // TODO
@@ -27,8 +30,14 @@ class DashInitializePodViewModel @Inject constructor(
override fun doExecuteAction(): Single =
Single.create { source ->
- // TODO use configured value for low reservoir trigger
- val disposable = omnipodManager.activatePodPart1(AlertTrigger.ReservoirVolumeTrigger(200)).subscribeBy(
+ val lowReservoirAlertEnabled = sp.getBoolean(R.string.key_omnipod_common_low_reservoir_alert_enabled, true)
+ val lowReservoirAlertUnits = sp.getInt(R.string.key_omnipod_common_low_reservoir_alert_units, 10)
+ val lowReservoirAlertTrigger = if (lowReservoirAlertEnabled) {
+ AlertTrigger.ReservoirVolumeTrigger((lowReservoirAlertUnits * 10).toShort())
+ } else
+ null
+
+ val disposable = omnipodManager.activatePodPart1(lowReservoirAlertTrigger).subscribeBy(
onNext = { podEvent ->
logger.debug(
LTag.PUMP,
@@ -41,6 +50,7 @@ class DashInitializePodViewModel @Inject constructor(
},
onComplete = {
logger.debug("Pod activation part 1 completed")
+ podStateManager.updateLowReservoirAlertSettings(lowReservoirAlertEnabled, lowReservoirAlertUnits)
source.onSuccess(PumpEnactResult(injector).success(true))
}
)
diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInsertCannulaViewModel.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInsertCannulaViewModel.kt
index 143fb8fb91..4173fcd4c7 100644
--- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInsertCannulaViewModel.kt
+++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInsertCannulaViewModel.kt
@@ -8,12 +8,16 @@ import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.PumpSync
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
+import info.nightscout.androidaps.plugins.bus.RxBusWrapper
+import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
+import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.action.InsertCannulaViewModel
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.pod.state.OmnipodDashPodStateManager
import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.mapProfileToBasalProgram
+import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.Single
import io.reactivex.rxkotlin.subscribeBy
import javax.inject.Inject
@@ -23,6 +27,9 @@ class DashInsertCannulaViewModel @Inject constructor(
private val profileFunction: ProfileFunction,
private val pumpSync: PumpSync,
private val podStateManager: OmnipodDashPodStateManager,
+ private val rxBus: RxBusWrapper,
+ private val sp: SP,
+
injector: HasAndroidInjector,
logger: AAPSLogger
) : InsertCannulaViewModel(injector, logger) {
@@ -45,7 +52,15 @@ class DashInsertCannulaViewModel @Inject constructor(
profile,
basalProgram
)
- val disposable = omnipodManager.activatePodPart2(basalProgram).subscribeBy(
+ val expirationReminderEnabled = sp.getBoolean(R.string.key_omnipod_common_expiration_reminder_enabled, true)
+ val expirationHours = sp.getInt(R.string.key_omnipod_common_expiration_reminder_hours_before_shutdown, 9)
+
+ val expirationHoursBeforeShutdown = if (expirationReminderEnabled)
+ expirationHours.toLong()
+ else
+ null
+
+ val disposable = omnipodManager.activatePodPart2(basalProgram, expirationHoursBeforeShutdown).subscribeBy(
onNext = { podEvent ->
logger.debug(
LTag.PUMP,
@@ -58,6 +73,7 @@ class DashInsertCannulaViewModel @Inject constructor(
},
onComplete = {
logger.debug("Pod activation part 2 completed")
+ podStateManager.basalProgram = basalProgram
pumpSync.connectNewPump()
pumpSync.insertTherapyEventIfNewWithTimestamp(
timestamp = System.currentTimeMillis(),
@@ -71,6 +87,14 @@ class DashInsertCannulaViewModel @Inject constructor(
pumpType = PumpType.OMNIPOD_DASH,
pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a"
)
+ pumpSync.syncStopTemporaryBasalWithPumpId(
+ timestamp = System.currentTimeMillis(),
+ endPumpId = System.currentTimeMillis(),
+ pumpType = PumpType.OMNIPOD_DASH,
+ pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a"
+ )
+ podStateManager.updateExpirationAlertSettings(expirationReminderEnabled, expirationHours)
+ rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_NOT_ATTACHED))
source.onSuccess(PumpEnactResult(injector).success(true))
}
)
diff --git a/omnipod-dash/src/main/res/xml/omnipod_dash_preferences.xml b/omnipod-dash/src/main/res/xml/omnipod_dash_preferences.xml
index 26d400e602..0e1d2dd11a 100644
--- a/omnipod-dash/src/main/res/xml/omnipod_dash_preferences.xml
+++ b/omnipod-dash/src/main/res/xml/omnipod_dash_preferences.xml
@@ -65,11 +65,33 @@
validate:maxNumber="50"
validate:minNumber="5"
validate:testType="numericRange" />
+
+
+
+
+ android:key="@string/key_omnipod_common_notification_uncertain_tbr_sound_enabled"
+ android:title="@string/omnipod_common_preferences_notification_uncertain_tbr_sound_enabled" />
+
+
+
+