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 6851f3a7ff..df8f87a507 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 @@ -1,6 +1,8 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash import android.content.Context +import android.os.Handler +import android.os.Looper import dagger.android.HasAndroidInjector import info.nightscout.androidaps.activities.ErrorHelperActivity.Companion.runAlarm import info.nightscout.androidaps.data.DetailedBolusInfo @@ -19,6 +21,7 @@ import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotifi import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress import info.nightscout.androidaps.plugins.general.overview.notifications.Notification import info.nightscout.androidaps.plugins.pump.common.defs.PumpType +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 @@ -67,10 +70,14 @@ class OmnipodDashPumpPlugin @Inject constructor( commandQueue: CommandQueueProvider ) : PumpPluginBase(pluginDescription, injector, aapsLogger, resourceHelper, commandQueue), Pump { @Volatile var bolusCanceled = false + private val handler: Handler = Handler(Looper.getMainLooper()) + lateinit private var statusChecker: Runnable + var nextPodWarningCheck : Long = 0 companion object { private const val BOLUS_RETRY_INTERVAL_MS = 2000.toLong() - private const val BOLUS_RETRIES = 5 // numer of retries for cancel/get bolus status + private const val BOLUS_RETRIES = 5 // number of retries for cancel/get bolus status + private const val STATUS_CHECK_INTERVAL_MS = (60L * 1000) private val pluginDescription = PluginDescription() .mainType(PluginType.PUMP) @@ -84,6 +91,52 @@ class OmnipodDashPumpPlugin @Inject constructor( private val pumpDescription = PumpDescription(PumpType.OMNIPOD_DASH) } + init { + statusChecker = Runnable { + refreshStatusOnUnacknowledgedCommands() + updatePodWarnings() + handler.postDelayed(statusChecker, STATUS_CHECK_INTERVAL_MS) + } + } + + private fun updatePodWarnings() { + if (System.currentTimeMillis() > nextPodWarningCheck) { + if (!podStateManager.isPodRunning) { + val notification = + Notification( + Notification.OMNIPOD_POD_NOT_ATTACHED, + "Pod not activated", + Notification.NORMAL + ) + rxBus.send(EventNewNotification(notification)) + } else { + rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_NOT_ATTACHED)) + if (podStateManager.isSuspended) { + val notification = + Notification( + Notification.OMNIPOD_POD_SUSPENDED, + "Insulin delivery suspended", + Notification.NORMAL + ) + rxBus.send(EventNewNotification(notification)) + } else { + rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED)) + // TODO: time out of sync notification? + } + } + nextPodWarningCheck = DateTimeUtil.getTimeInFutureFromMinutes(15) + } + } + + private fun refreshStatusOnUnacknowledgedCommands() { + if (podStateManager.isPodRunning && + podStateManager.activeCommand != null && + commandQueue.size() == 0 && + commandQueue.performing() == null) { + commandQueue.readStatus("Unconfirmed command", null) + } + } + override fun isInitialized(): Boolean { // TODO return true @@ -129,8 +182,11 @@ class OmnipodDashPumpPlugin @Inject constructor( // TODO } + + override fun getPumpStatus(reason: String) { - if (reason != "REQUESTED BY USER" && !podStateManager.isActivationCompleted) { + aapsLogger.debug(LTag.PUMP, "getPumpStatus reason=$reason") + if (reason != "REQUESTED BY USER" && !podStateManager.isActivationCompleted) { // prevent races on BLE when the pod is not activated return } @@ -140,6 +196,13 @@ class OmnipodDashPumpPlugin @Inject constructor( aapsLogger.error(LTag.PUMP, "Error in getPumpStatus", throwable) } else { aapsLogger.info(LTag.PUMP, "getPumpStatus executed with success") + if (!podStateManager.isActivationCompleted) { + val msg = podStateManager.recoverActivationFromPodStatus() + msg?.let { + // TODO: show dialog with "try again, the pod is busy now" + aapsLogger.info(LTag.PUMP, "recoverActivationFromPodStatus msg=$msg") + } + } } } @@ -201,6 +264,7 @@ class OmnipodDashPumpPlugin @Inject constructor( if (!podStateManager.isActivationCompleted) { return PumpEnactResult(injector).success(true).enacted(true) } + aapsLogger.debug(LTag.PUMP, "setNewBasalProfile profile=$profile") val basalProgram = mapProfileToBasalProgram(profile) var deliverySuspended = false return executeProgrammingCommand( @@ -262,7 +326,7 @@ class OmnipodDashPumpPlugin @Inject constructor( rxBus.send(EventTempBasalChange()) } .ignoreElements() - ).doFinally { + ).doOnComplete { notifyOnUnconfirmed( Notification.FAILED_UPDATE_PROFILE, "Suspend delivery is unconfirmed! " + @@ -272,16 +336,15 @@ class OmnipodDashPumpPlugin @Inject constructor( } } - /* override fun onStop() { - super.onStop() - disposable.clear() - } - - */ - override fun onStart() { super.onStart() podStateManager.onStart() + handler.postDelayed(statusChecker, STATUS_CHECK_INTERVAL_MS) + } + + override fun onStop() { + super.onStop() + handler.removeCallbacks(statusChecker) } private fun observeDeliverySuspended(): Completable = Completable.defer { @@ -673,10 +736,11 @@ class OmnipodDashPumpPlugin @Inject constructor( executeProgrammingCommand( historyEntry = history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL), command = omnipodManager.stopTempBasal(hasTempBasalBeepEnabled()).ignoreElements() - ).doFinally { + ).doOnComplete { notifyOnUnconfirmed( Notification.OMNIPOD_TBR_ALERTS, - "Setting temp basal failed. If a temp basal was previously running, it might have been cancelled. " + + "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, ) @@ -734,6 +798,7 @@ class OmnipodDashPumpPlugin @Inject constructor( private fun notifyOnUnconfirmed(notificationId: Int, msg: String, sound: Int?) { if (podStateManager.activeCommand != null) { + aapsLogger.debug(LTag.PUMP, "Notification for active command: ${podStateManager.activeCommand}") showNotification(notificationId, msg, Notification.URGENT, sound) } } @@ -877,7 +942,7 @@ class OmnipodDashPumpPlugin @Inject constructor( .ignoreElements() ).doFinally { notifyOnUnconfirmed( - Notification.PUMP_ERROR, + Notification.FAILED_UPDATE_PROFILE, "Unconfirmed resumeDelivery command. Please refresh pod status", R.raw.boluserror ) @@ -972,7 +1037,18 @@ class OmnipodDashPumpPlugin @Inject constructor( val historyEntry = history.getById(command.historyId) aapsLogger.debug(LTag.PUMPCOMM, "handling command confirmation: $confirmation") when (historyEntry.commandType) { - OmnipodCommandType.CANCEL_TEMPORARY_BASAL, + OmnipodCommandType.CANCEL_TEMPORARY_BASAL -> { + if (confirmation.success) { + pumpSync.syncStopTemporaryBasalWithPumpId( + historyEntry.createdAt, + historyEntry.pumpId(), + PumpType.OMNIPOD_DASH, + serialNumber() + ) + podStateManager.tempBasal = null + } + rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS)) + } OmnipodCommandType.RESUME_DELIVERY -> { // We can't invalidate this command, // and this is why it is pumpSync-ed at this point @@ -984,6 +1060,7 @@ class OmnipodDashPumpPlugin @Inject constructor( serialNumber() ) podStateManager.tempBasal = null + rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED)) } rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS)) } @@ -1004,8 +1081,10 @@ class OmnipodDashPumpPlugin @Inject constructor( PumpType.OMNIPOD_DASH, serialNumber() ) + 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_TBR_ALERTS)) } OmnipodCommandType.SET_TEMPORARY_BASAL -> { 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 78b778def9..487ecf2c6c 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 @@ -173,8 +173,7 @@ class OmnipodDashManagerImpl @Inject constructor( DefaultStatusResponse::class ) }.doOnComplete { - // TODO: remove podStateManager.basalProgram? - podStateManager.basalProgram = basalProgram + podStateManager.timeZone = TimeZone.getDefault() } } @@ -246,6 +245,7 @@ class OmnipodDashManagerImpl @Inject constructor( observeVerifyPrime.doOnComplete(ActivationProgressUpdater(ActivationProgress.PRIME_COMPLETED)) ) } + if (podStateManager.activationProgress.isBefore(ActivationProgress.PRIMING)) { observables.add(observeConnectToPod) // connection can time out while waiting observables.add( @@ -270,6 +270,7 @@ class OmnipodDashManagerImpl @Inject constructor( }.doOnComplete(ActivationProgressUpdater(ActivationProgress.PRIMING)) ) } + if (podStateManager.activationProgress.isBefore(ActivationProgress.REPROGRAMMED_LUMP_OF_COAL_ALERT)) { observables.add( observeSendProgramAlertsCommand( @@ -304,6 +305,7 @@ class OmnipodDashManagerImpl @Inject constructor( ).doOnComplete(ActivationProgressUpdater(ActivationProgress.PROGRAMMED_LOW_RESERVOIR_ALERTS)) ) } + if (podStateManager.activationProgress.isBefore(ActivationProgress.SET_UNIQUE_ID)) { observables.add( observeSendSetUniqueIdCommand.doOnComplete(ActivationProgressUpdater(ActivationProgress.SET_UNIQUE_ID)) 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 284b6d975d..664db805e0 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 @@ -21,6 +21,7 @@ class ServiceDiscoverer( */ fun discoverServices(): Map { logger.debug(LTag.PUMPBTCOMM, "Discovering services") + bleCallbacks.startServiceDiscovery() val discover = gatt.discoverServices() if (!discover) { throw ConnectException("Could not start discovering services`") 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 de2fceafe9..3d4d3d1339 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 @@ -61,6 +61,10 @@ class BleCommCallbacks( } } + fun startServiceDiscovery() { + serviceDiscoveryComplete = CountDownLatch(1) + } + fun waitForServiceDiscovery(timeoutMs: Int) { try { serviceDiscoveryComplete.await(timeoutMs.toLong(), TimeUnit.MILLISECONDS) 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 15fcd4f84e..3eeab834ad 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 @@ -35,11 +35,12 @@ interface OmnipodDashPodStateManager { val isPodKaput: Boolean var bluetoothConnectionState: BluetoothConnectionState - var timeZone: DateTimeZone + var timeZone: TimeZone val lastUpdatedSystem: Long // System.currentTimeMillis() val lastStatusResponseReceived: Long val time: DateTime? - val timeBehind: Duration? + val timeDrift: Duration? + val expiry: DateTime? val messageSequenceNumber: Short val sequenceNumberOfLastProgrammingCommand: Short? @@ -96,6 +97,12 @@ interface OmnipodDashPodStateManager { fun createLastBolus(requestedUnits: Double, historyId: String, bolusType: DetailedBolusInfo.BolusType) fun markLastBolusComplete(): LastBolus? fun onStart() + /* + This is called only:. It overwrites activationStatus + - when activation was interrupted(application crash, killed, etc) + - after getPodStatus was successful(we have an up-to-date podStatus) + */ + fun recoverActivationFromPodStatus(): String? 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 2da1889ba9..95df4367a7 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 @@ -99,10 +99,10 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( } } - override var timeZone: DateTimeZone - get() = podState.timeZone + override var timeZone: TimeZone + get() = TimeZone.getTimeZone(podState.timeZone) set(tz) { - podState.timeZone = tz + podState.timeZone = tz.getDisplayName(true, TimeZone.SHORT) store() } @@ -189,16 +189,27 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( if ((activationTime != null) && (minutesSinceActivation != null)) { return DateTime(activationTime) .plusMinutes(minutesSinceActivation.toInt()) - .plus(Duration(podState.lastStatusResponseReceived, System.currentTimeMillis())) + .plus(Duration(podState.lastUpdatedSystem, System.currentTimeMillis())) } return null } - override val timeBehind: Duration? + override val timeDrift: Duration? get() { return Duration(DateTime.now(), time) } + override val expiry: DateTime? + // TODO: Consider storing expiry datetime in pod state saving continuously recalculating to the same value + get() { + val podLifeInHours = podLifeInHours + val activationTime = podState.activationTime + if (podLifeInHours != null && activationTime != null) { + return DateTime(podState.activationTime).plusHours(podLifeInHours.toInt()) + } + return null + } + override var bluetoothConnectionState: OmnipodDashPodStateManager.BluetoothConnectionState get() = podState.bluetoothConnectionState set(bluetoothConnectionState) { @@ -312,6 +323,29 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( } } + override fun recoverActivationFromPodStatus(): String? { + val newActivationProgress = when (podState.podStatus) { + PodStatus.FILLED -> + ActivationProgress.NOT_STARTED + PodStatus.UID_SET -> + ActivationProgress.SET_UNIQUE_ID + PodStatus.ENGAGING_CLUTCH_DRIVE, PodStatus.PRIMING -> + return "Busy" + PodStatus.CLUTCH_DRIVE_ENGAGED -> + ActivationProgress.PRIME_COMPLETED + PodStatus.BASAL_PROGRAM_SET -> + ActivationProgress.PROGRAMMED_BASAL + PodStatus.RUNNING_ABOVE_MIN_VOLUME, PodStatus.RUNNING_BELOW_MIN_VOLUME -> + ActivationProgress.CANNULA_INSERTED + else -> + null + } + newActivationProgress?.let { + podState.activationProgress = it + } + return null + } + @Synchronized override fun updateActiveCommand() = Maybe.create { source -> val activeCommand = podState.activeCommand @@ -402,6 +436,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( podState.lastStatusResponseReceived = now + 2 podState.activeCommand = newCommand } + CommandSendingNotConfirmed -> { val now = System.currentTimeMillis() val newCommand = podState.activeCommand?.copy( @@ -410,10 +445,10 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( ) podState.lastStatusResponseReceived = 0 } + CommandSendingFailure, NoActiveCommand -> podState.activeCommand = null } - } override fun updateFromDefaultStatusResponse(response: DefaultStatusResponse) { @@ -431,6 +466,9 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( podState.lastUpdatedSystem = System.currentTimeMillis() podState.lastStatusResponseReceived = SystemClock.elapsedRealtime() updateLastBolusFromResponse(response.bolusPulsesRemaining) + if (podState.activationTime == null) { + podState.activationTime = System.currentTimeMillis() - (response.minutesSinceActivation * 60000) + } store() rxBus.send(EventOmnipodDashPumpValuesChanged()) @@ -479,12 +517,6 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( podState.uniqueId = response.uniqueIdReceivedInCommand podState.lastUpdatedSystem = System.currentTimeMillis() - // TODO: what is considered to be the pod activation time? - // LTK negotiation ? - // setUniqueId? - // compute it from the number of "minutesOnPod"? - podState.activationTime = System.currentTimeMillis() - store() rxBus.send(EventOmnipodDashPumpValuesChanged()) } @@ -565,7 +597,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( var ltk: ByteArray? = null var eapAkaSequenceNumber: Long = 1 var bolusPulsesRemaining: Short = 0 - var timeZone = DateTimeZone.getDefault() + var timeZone: String = "" // TimeZone ID (e.g. "Europe/Amsterdam") var bleVersion: SoftwareVersion? = null var firmwareVersion: SoftwareVersion? = null diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/DashPodManagementActivity.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/DashPodManagementActivity.kt index e3f6f97138..3d89be5da8 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/DashPodManagementActivity.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/DashPodManagementActivity.kt @@ -125,7 +125,7 @@ class DashPodManagementActivity : NoSplashAppCompatActivity() { binding.buttonActivatePod.isEnabled = podStateManager.activationProgress.isBefore(ActivationProgress.COMPLETED) binding.buttonDeactivatePod.isEnabled = - podStateManager.activationProgress.isAtLeast(ActivationProgress.SET_UNIQUE_ID) || + podStateManager.ltk != null || podStateManager.podStatus == PodStatus.ALARM if (podStateManager.activationProgress.isAtLeast(ActivationProgress.PHASE_1_COMPLETED)) { 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 b9d0103e8c..59e90ef7dc 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 @@ -45,7 +45,6 @@ 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.DateTimeZone import org.joda.time.Duration import java.util.* import javax.inject.Inject @@ -69,7 +68,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() { private const val REFRESH_INTERVAL_MILLIS = 15 * 1000L // 15 seconds private const val PLACEHOLDER = "-" - private const val MAX_TIME_DEVIATION_MINUTES = 15L + private const val MAX_TIME_DEVIATION_MINUTES = 10L } private var disposables: CompositeDisposable = CompositeDisposable() @@ -237,31 +236,6 @@ class OmnipodDashOverviewFragment : DaggerFragment() { } } - // Get time on pod from activation time and minutes since activation - private fun getTimeOnPod(): DateTime? { - var timeOnPod: DateTime? = null - val minutesSinceActivation = podStateManager.minutesSinceActivation - val activationTime = podStateManager.activationTime - if ((activationTime != null) && (minutesSinceActivation != null)) { - timeOnPod = DateTime(activationTime) - .plusMinutes(minutesSinceActivation.toInt()) - .plus(Duration(podStateManager.lastStatusResponseReceived, System.currentTimeMillis())) - } - return timeOnPod - } - - // TODO: Consider storing expiry datetime in pod state saving continuesly recalculating to the same value - private fun getExpiryAt(): DateTime? { - var expiresAt: DateTime? = null - val podLifeInHours = podStateManager.podLifeInHours - val minutesSinceActivation = podStateManager.minutesSinceActivation - if (podLifeInHours != null && minutesSinceActivation != null) { - val expiresInMinutes = podLifeInHours * 60 - minutesSinceActivation - expiresAt = DateTime().plusMinutes(expiresInMinutes) - } - return expiresAt - } - private fun updateOmnipodStatus() { updateLastConnection() updateLastBolus() @@ -294,35 +268,34 @@ class OmnipodDashOverviewFragment : DaggerFragment() { ) // Update time on Pod - // TODO: For now: derive from podStateManager.minutesSinceActivation - val timeOnPod = getTimeOnPod() - podInfoBinding.timeOnPod.text = podStateManager.time?.let{ + podInfoBinding.timeOnPod.text = podStateManager.time?.let { readableZonedTime(it) } ?: PLACEHOLDER podInfoBinding.timeOnPod.setTextColor( - podStateManager.timeBehind?.let { + podStateManager.timeDrift?.let { if (it.abs().isLongerThan(Duration.standardMinutes(MAX_TIME_DEVIATION_MINUTES))) { Color.RED - }else { + } else { Color.WHITE } } ?: Color.WHITE ) - // Update Pod expiry time - val expiresAt = getExpiryAt() + 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.setTextColor( + if (DateTime.now().isAfter(expiresAt)) { + Color.RED + } else { + Color.WHITE + } + ) } podStateManager.alarmType?.let { @@ -413,9 +386,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() { podInfoBinding.lastConnection.setTextColor(lastConnectionColor) } else { podInfoBinding.lastConnection.setTextColor(Color.WHITE) - podInfoBinding.lastConnection.text = readableDuration( - Duration(podStateManager.lastUpdatedSystem, System.currentTimeMillis()) - ) + podInfoBinding.lastConnection.text = PLACEHOLDER } } @@ -552,7 +523,6 @@ class OmnipodDashOverviewFragment : DaggerFragment() { private fun updateRefreshStatusButton() { buttonBinding.buttonRefreshStatus.isEnabled = podStateManager.isUniqueIdSet && - podStateManager.activationProgress.isAtLeast(ActivationProgress.PHASE_1_COMPLETED) && isQueueEmpty() } @@ -630,25 +600,41 @@ class OmnipodDashOverviewFragment : DaggerFragment() { } } - private fun getTimeZone(): DateTimeZone { - // TODO: Get timezone as configured/podState - // return getSafe(() -> podState.getTimeZone()); - return DateTimeZone.getDefault() + // 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) - val timeZone = getTimeZone().toTimeZone() - if (timeZone == TimeZone.getDefault()) { - return dateUtil.dateAndTimeString(timeAsJavaData.time) - } - - // 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) + // // 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 { 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 c7bc85a539..143fb8fb91 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 @@ -2,13 +2,17 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.wizard.activatio import androidx.annotation.StringRes import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.PumpEnactResult 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.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 io.reactivex.Single import io.reactivex.rxkotlin.subscribeBy @@ -17,6 +21,8 @@ import javax.inject.Inject class DashInsertCannulaViewModel @Inject constructor( private val omnipodManager: OmnipodDashManager, private val profileFunction: ProfileFunction, + private val pumpSync: PumpSync, + private val podStateManager: OmnipodDashPodStateManager, injector: HasAndroidInjector, logger: AAPSLogger ) : InsertCannulaViewModel(injector, logger) { @@ -52,6 +58,19 @@ class DashInsertCannulaViewModel @Inject constructor( }, onComplete = { logger.debug("Pod activation part 2 completed") + pumpSync.connectNewPump() + pumpSync.insertTherapyEventIfNewWithTimestamp( + timestamp = System.currentTimeMillis(), + type = DetailedBolusInfo.EventType.CANNULA_CHANGE, + pumpType = PumpType.OMNIPOD_DASH, + pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a" + ) + pumpSync.insertTherapyEventIfNewWithTimestamp( + timestamp = System.currentTimeMillis(), + type = DetailedBolusInfo.EventType.INSULIN_CHANGE, + pumpType = PumpType.OMNIPOD_DASH, + pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a" + ) source.onSuccess(PumpEnactResult(injector).success(true)) } )