diff --git a/.github/workflows/gradle-ci-workflow.yaml b/.github/workflows/gradle-ci-workflow.yaml new file mode 100644 index 0000000000..6ec6107b54 --- /dev/null +++ b/.github/workflows/gradle-ci-workflow.yaml @@ -0,0 +1,33 @@ +name: Gradle CI + +on: + - push + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Create NDK path + run: sudo mkdir -p /usr/local/lib/android/sdk/ndk && sudo chmod 777 /usr/local/lib/android/sdk/ndk + - name: Cache NDKs + id: cache-ndk + uses: actions/cache@v2 + with: + path: /usr/local/lib/android/sdk/ndk + key: ${{ runner.os }}-ndk-21.0.6113669-21.1.6352462 + - name: Install NDK 21.0.6113669 + run: echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;21.0.6113669" --sdk_root=${ANDROID_SDK_ROOT} + - name: Install NDK 21.1.6352462 + run: echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;21.1.6352462" --sdk_root=${ANDROID_SDK_ROOT} + - uses: eskatos/gradle-command-action@v1 + with: + arguments: assembleFullDebug + wrapper-cache-enabled: true + dependencies-cache-enabled: true + configuration-cache-enabled: true diff --git a/app/build.gradle b/app/build.gradle index c633d46524..4ce21e51c3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -126,7 +126,7 @@ android { targetSdkVersion 28 multiDexEnabled true versionCode 1500 - version "2.6.7-dev" + version "2.7-omnipod-0.4.1-SNAPSHOT" buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' @@ -245,6 +245,7 @@ dependencies { implementation project(':danar') implementation project(':rileylink') implementation project(':medtronic') + implementation project(':omnipod') implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.google.android.gms:play-services-wearable:17.0.0' diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt index a748e57eb1..0c98864fb4 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt @@ -9,6 +9,8 @@ import info.nightscout.androidaps.core.di.CoreModule import info.nightscout.androidaps.dana.di.DanaModule import info.nightscout.androidaps.danar.di.DanaRModule import info.nightscout.androidaps.danars.di.DanaRSModule +import info.nightscout.androidaps.plugins.pump.common.dagger.RileyLinkModule +import info.nightscout.androidaps.plugins.pump.omnipod.dagger.OmnipodModule import javax.inject.Singleton @Singleton @@ -28,6 +30,7 @@ import javax.inject.Singleton WizardModule::class, RileyLinkModule::class, MedtronicModule::class, + OmnipodModule::class, APSModule::class, PreferencesModule::class, OverviewModule::class, diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt index 07c8cd1495..ba077452fb 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt @@ -34,6 +34,7 @@ import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyL import info.nightscout.androidaps.plugins.pump.insight.LocalInsightFragment import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicFragment import info.nightscout.androidaps.plugins.pump.medtronic.dialog.RileyLinkStatusDeviceMedtronic +import info.nightscout.androidaps.plugins.pump.omnipod.ui.OmnipodFragment import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpFragment import info.nightscout.androidaps.plugins.source.BGSourceFragment import info.nightscout.androidaps.plugins.treatments.TreatmentsFragment @@ -65,6 +66,7 @@ abstract class FragmentsModule { @ContributesAndroidInjector abstract fun contributesLoopFragment(): LoopFragment @ContributesAndroidInjector abstract fun contributesMaintenanceFragment(): MaintenanceFragment @ContributesAndroidInjector abstract fun contributesMedtronicFragment(): MedtronicFragment + @ContributesAndroidInjector abstract fun contributesOmnipodFragment(): OmnipodFragment @ContributesAndroidInjector abstract fun contributesNSProfileFragment(): NSProfileFragment @ContributesAndroidInjector abstract fun contributesNSClientFragment(): NSClientFragment @ContributesAndroidInjector abstract fun contributesSmsCommunicatorFragment(): SmsCommunicatorFragment diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt index c7033eec2d..efb59be7c3 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt @@ -40,6 +40,7 @@ import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin +import info.nightscout.androidaps.plugins.pump.omnipod.OmnipodPumpPlugin import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin @@ -147,6 +148,12 @@ abstract class PluginsModule { @IntKey(150) abstract fun bindMedtronicPumpPlugin(plugin: MedtronicPumpPlugin): PluginBase + @Binds + @PumpDriver + @IntoMap + @IntKey(155) + abstract fun bindOmnipodPumpPlugin(plugin: OmnipodPumpPlugin): PluginBase + @Binds @NotNSClient @IntoMap diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ServicesModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ServicesModule.kt index ec71d8a4ad..6ba01130ae 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ServicesModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ServicesModule.kt @@ -10,6 +10,7 @@ import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.Riley import info.nightscout.androidaps.plugins.pump.insight.InsightAlertService import info.nightscout.androidaps.plugins.pump.insight.connection_service.InsightConnectionService import info.nightscout.androidaps.plugins.pump.medtronic.service.RileyLinkMedtronicService +import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.service.RileyLinkOmnipodService import info.nightscout.androidaps.services.AlarmSoundService import info.nightscout.androidaps.services.DataService import info.nightscout.androidaps.services.LocationService @@ -29,4 +30,5 @@ abstract class ServicesModule { @ContributesAndroidInjector abstract fun contributesInsightConnectionService(): InsightConnectionService @ContributesAndroidInjector abstract fun contributesRileyLinkService(): RileyLinkService @ContributesAndroidInjector abstract fun contributesRileyLinkMedtronicService(): RileyLinkMedtronicService + @ContributesAndroidInjector abstract fun contributesRileyLinkOmnipodService(): RileyLinkOmnipodService } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt index 20a6493ea8..900b15bde6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt @@ -25,7 +25,6 @@ import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction import info.nightscout.androidaps.plugins.general.overview.StatusLightHandler import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.androidaps.utils.ui.SingleClickButton import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.extensions.plusAssign @@ -33,6 +32,7 @@ import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP +import info.nightscout.androidaps.utils.ui.SingleClickButton import info.nightscout.androidaps.utils.ui.UIRunnable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable @@ -236,6 +236,7 @@ class ActionsFragment : DaggerFragment() { actions_historybrowser.visibility = (profile != null).toVisibility() actions_fill?.visibility = (pump.pumpDescription.isRefillingCapable && pump.isInitialized && !pump.isSuspended).toVisibility() + actions_pumpbatterychange?.visibility = pump.pumpDescription.isBatteryReplaceable.toVisibility() actions_temptarget?.visibility = (profile != null && config.APS).toVisibility() actions_tddstats?.visibility = pump.pumpDescription.supportsTDDs.toVisibility() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.java index 44550ebea6..dbc8823395 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.java @@ -11,7 +11,6 @@ import javax.inject.Inject; import javax.inject.Singleton; import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.BuildConfig; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.DetailedBolusInfo; import info.nightscout.androidaps.data.Profile; @@ -69,6 +68,7 @@ public class MDIPlugin extends PumpPluginBase implements PumpInterface { pumpDescription.isTempBasalCapable = false; pumpDescription.isSetBasalProfileCapable = false; pumpDescription.isRefillingCapable = false; + pumpDescription.isBatteryReplaceable = false; } @Override diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/PumpDescription.java b/core/src/main/java/info/nightscout/androidaps/interfaces/PumpDescription.java index 76c6253160..92de42542a 100644 --- a/core/src/main/java/info/nightscout/androidaps/interfaces/PumpDescription.java +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/PumpDescription.java @@ -11,11 +11,11 @@ import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; public class PumpDescription { public PumpType pumpType = PumpType.GenericAAPS; - public PumpDescription () { + public PumpDescription() { resetSettings(); } - public PumpDescription (PumpType pumpType) { + public PumpDescription(PumpType pumpType) { this(); setPumpDescription(pumpType); } @@ -52,6 +52,7 @@ public class PumpDescription { public double basalMaximumRate; public boolean isRefillingCapable; + public boolean isBatteryReplaceable; public boolean storesCarbInfo; @@ -88,7 +89,8 @@ public class PumpDescription { basalMaximumRate = 25d; is30minBasalRatesCapable = false; - isRefillingCapable = false; + isRefillingCapable = true; + isBatteryReplaceable = true; storesCarbInfo = true; supportsTDDs = false; @@ -136,6 +138,7 @@ public class PumpDescription { basalMinimumRate = pumpType.getBaseBasalMinValue(); isRefillingCapable = pumpCapability.hasCapability(PumpCapability.Refill); + isBatteryReplaceable = pumpCapability.hasCapability(PumpCapability.ReplaceBattery); storesCarbInfo = pumpCapability.hasCapability(PumpCapability.StoreCarbInfo); supportsTDDs = pumpCapability.hasCapability(PumpCapability.TDD); diff --git a/core/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/Notification.java b/core/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/Notification.java index 52495a2d41..b4d91226ee 100644 --- a/core/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/Notification.java +++ b/core/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/Notification.java @@ -73,6 +73,7 @@ public class Notification { public static final int CARBS_REQUIRED = 60; public static final int IMPORTANCE_HIGH = 2; public static final int OMNIPOD_POD_SUSPENDED = 61; + public static final int OMNIPOD_POD_ALERTS_UPDATED = 62; public static final String CATEGORY_ALARM = "alarm"; diff --git a/core/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpCapability.java b/core/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpCapability.java index 8917d3e834..0f8baa6392 100644 --- a/core/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpCapability.java +++ b/core/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpCapability.java @@ -11,18 +11,19 @@ public enum PumpCapability { TempBasal, // isTempBasalCapable BasalProfileSet, // isSetBasalProfileCapable Refill, // isRefillingCapable + ReplaceBattery, // isBatteryReplaceable StoreCarbInfo, // storesCarbInfo TDD, // supportsTDDs ManualTDDLoad, // needsManualTDDLoad BasalRate30min, // is30minBasalRatesCapable // grouped by pump - VirtualPumpCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill), // - ComboCapabilities(Bolus, TempBasal, BasalProfileSet, Refill, TDD, ManualTDDLoad), // - DanaCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, TDD, ManualTDDLoad), // - DanaWithHistoryCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, StoreCarbInfo, TDD, ManualTDDLoad), // - InsightCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill,TDD,BasalRate30min), // - MedtronicCapabilities(Bolus, TempBasal, BasalProfileSet, Refill, TDD), // + VirtualPumpCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, ReplaceBattery), // + ComboCapabilities(Bolus, TempBasal, BasalProfileSet, Refill, ReplaceBattery, TDD, ManualTDDLoad), // + DanaCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, ReplaceBattery, TDD, ManualTDDLoad), // + DanaWithHistoryCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, ReplaceBattery, StoreCarbInfo, TDD, ManualTDDLoad), // + InsightCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, ReplaceBattery, TDD, BasalRate30min), // + MedtronicCapabilities(Bolus, TempBasal, BasalProfileSet, Refill, ReplaceBattery, TDD), // OmnipodCapabilities(Bolus, TempBasal, BasalProfileSet, BasalRate30min), // // BasalRates (separately grouped) @@ -35,19 +36,16 @@ public enum PumpCapability { PumpCapability[] children; - PumpCapability() - { + PumpCapability() { } - PumpCapability(PumpCapability...children) - { + PumpCapability(PumpCapability... children) { this.children = children; } - public boolean hasCapability(PumpCapability capability) - { + public boolean hasCapability(PumpCapability capability) { // we can only check presense of simple capabilities if (capability.children != null) return false; @@ -55,19 +53,16 @@ public enum PumpCapability { if (this == capability) return true; - if (this.children!=null) - { + if (this.children != null) { for (PumpCapability child : children) { if (child == capability) return true; } return false; - } - else + } else return false; } - } diff --git a/core/src/main/java/info/nightscout/androidaps/utils/textValidator/DefaultEditTextValidator.kt b/core/src/main/java/info/nightscout/androidaps/utils/textValidator/DefaultEditTextValidator.kt index 2956a8d2b8..94f3cfd3b6 100644 --- a/core/src/main/java/info/nightscout/androidaps/utils/textValidator/DefaultEditTextValidator.kt +++ b/core/src/main/java/info/nightscout/androidaps/utils/textValidator/DefaultEditTextValidator.kt @@ -1,7 +1,6 @@ package info.nightscout.androidaps.utils.textValidator import android.content.Context -import android.content.res.TypedArray import android.text.Editable import android.text.TextUtils import android.text.TextWatcher @@ -37,25 +36,20 @@ class DefaultEditTextValidator : EditTextValidator { resetValidators(context) } - constructor(editTextView: EditText, typedArray: TypedArray, context: Context) { - emptyAllowed = typedArray.getBoolean(R.styleable.FormEditText_emptyAllowed, false) - testType = typedArray.getInt(R.styleable.FormEditText_testType, EditTextValidator.TEST_NOCHECK) - testErrorString = typedArray.getString(R.styleable.FormEditText_testErrorString) - classType = typedArray.getString(R.styleable.FormEditText_classType) - customRegexp = typedArray.getString(R.styleable.FormEditText_customRegexp) - emptyErrorStringDef = typedArray.getString(R.styleable.FormEditText_emptyErrorString) - customFormat = typedArray.getString(R.styleable.FormEditText_customFormat) - if (testType == EditTextValidator.TEST_MIN_LENGTH) - minLength = typedArray.getInt(R.styleable.FormEditText_minLength, 0) - if (testType == EditTextValidator.TEST_NUMERIC_RANGE) { - minNumber = typedArray.getInt(R.styleable.FormEditText_minNumber, Int.MIN_VALUE) - maxNumber = typedArray.getInt(R.styleable.FormEditText_maxNumber, Int.MAX_VALUE) - } - if (testType == EditTextValidator.TEST_FLOAT_NUMERIC_RANGE) { - floatminNumber = typedArray.getFloat(R.styleable.FormEditText_floatminNumber, Float.MIN_VALUE) - floatmaxNumber = typedArray.getFloat(R.styleable.FormEditText_floatmaxNumber, Float.MAX_VALUE) - } - typedArray.recycle() + constructor(editTextView: EditText, parameters: Parameters, context: Context) { + emptyAllowed = parameters.emptyAllowed + testType = parameters.testType + testErrorString = parameters.testErrorString + classType = parameters.classType + customRegexp = parameters.customRegexp + emptyErrorStringDef = parameters.emptyErrorStringDef + customFormat = parameters.customFormat + minLength = parameters.minLength + minNumber = parameters.minNumber + maxNumber = parameters.maxNumber + floatminNumber = parameters.floatminNumber + floatmaxNumber = parameters.floatmaxNumber + setEditText(editTextView) resetValidators(context) } @@ -168,7 +162,7 @@ class DefaultEditTextValidator : EditTextValidator { } @Suppress("unused") - fun setClassType(classType: String?, testErrorString: String?, context: Context) : DefaultEditTextValidator{ + fun setClassType(classType: String?, testErrorString: String?, context: Context): DefaultEditTextValidator { testType = EditTextValidator.TEST_CUSTOM this.classType = classType this.testErrorString = testErrorString @@ -177,7 +171,7 @@ class DefaultEditTextValidator : EditTextValidator { } @Suppress("unused") - fun setCustomRegexp(customRegexp: String?, context: Context) : DefaultEditTextValidator { + fun setCustomRegexp(customRegexp: String?, context: Context): DefaultEditTextValidator { testType = EditTextValidator.TEST_REGEXP this.customRegexp = customRegexp resetValidators(context) @@ -185,13 +179,13 @@ class DefaultEditTextValidator : EditTextValidator { } @Suppress("unused") - fun setEmptyAllowed(emptyAllowed: Boolean, context: Context) : DefaultEditTextValidator { + fun setEmptyAllowed(emptyAllowed: Boolean, context: Context): DefaultEditTextValidator { this.emptyAllowed = emptyAllowed resetValidators(context) return this } - fun setEmptyErrorString(emptyErrorString: String?) : DefaultEditTextValidator { + fun setEmptyErrorString(emptyErrorString: String?): DefaultEditTextValidator { emptyErrorStringActual = if (!TextUtils.isEmpty(emptyErrorString)) { emptyErrorString } else { @@ -201,14 +195,14 @@ class DefaultEditTextValidator : EditTextValidator { } @Suppress("unused") - fun setTestErrorString(testErrorString: String?, context: Context) : DefaultEditTextValidator { + fun setTestErrorString(testErrorString: String?, context: Context): DefaultEditTextValidator { this.testErrorString = testErrorString resetValidators(context) return this } @Suppress("unused") - fun setTestType(testType: Int, context: Context) : DefaultEditTextValidator { + fun setTestType(testType: Int, context: Context): DefaultEditTextValidator { this.testType = testType resetValidators(context) return this @@ -248,4 +242,19 @@ class DefaultEditTextValidator : EditTextValidator { } catch (e: Throwable) { !TextUtils.isEmpty(editTextView.error) } + + class Parameters { + var testErrorString: String? = null + var emptyAllowed = false + var testType: Int = EditTextValidator.TEST_NOCHECK + var classType: String? = null + var customRegexp: String? = null + var customFormat: String? = null + var emptyErrorStringDef: String? = null + var minLength = 0 + var minNumber = 0 + var maxNumber = 0 + var floatminNumber = 0f + var floatmaxNumber = 0f + } } \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/utils/textValidator/ValidatingEditTextPreference.kt b/core/src/main/java/info/nightscout/androidaps/utils/textValidator/ValidatingEditTextPreference.kt index ae75641b4c..5bf97f6dee 100644 --- a/core/src/main/java/info/nightscout/androidaps/utils/textValidator/ValidatingEditTextPreference.kt +++ b/core/src/main/java/info/nightscout/androidaps/utils/textValidator/ValidatingEditTextPreference.kt @@ -1,22 +1,26 @@ package info.nightscout.androidaps.utils.textValidator -import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import androidx.preference.EditTextPreference -import androidx.preference.EditTextPreference.OnBindEditTextListener import androidx.preference.PreferenceViewHolder import info.nightscout.androidaps.core.R class ValidatingEditTextPreference(ctx: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : EditTextPreference(ctx, attrs, defStyleAttr, defStyleRes) { + private lateinit var validatorParameters: DefaultEditTextValidator.Parameters + private var validator: DefaultEditTextValidator? = null + init { - val typedArray = context.obtainStyledAttributes(attrs, R.styleable.FormEditText, 0, 0) - val onBindEditTextListener = OnBindEditTextListener { editText -> - editTextValidator = DefaultEditTextValidator(editText, typedArray, context) + obtainValidatorParameters(attrs) + + setOnBindEditTextListener { editText -> + validator = DefaultEditTextValidator(editText, validatorParameters, context) + } + setOnPreferenceChangeListener { preference, newValue -> + validator?.testValidity(false) ?: true } - setOnBindEditTextListener(onBindEditTextListener) } constructor(ctx: Context, attrs: AttributeSet, defStyle: Int) @@ -25,11 +29,32 @@ class ValidatingEditTextPreference(ctx: Context, attrs: AttributeSet, defStyleAt constructor(ctx: Context, attrs: AttributeSet) : this(ctx, attrs, R.attr.editTextPreferenceStyle) - private lateinit var editTextValidator: EditTextValidator - override fun onBindViewHolder(holder: PreferenceViewHolder?) { super.onBindViewHolder(holder) holder?.isDividerAllowedAbove = false holder?.isDividerAllowedBelow = false } + + private fun obtainValidatorParameters(attrs: AttributeSet) { + val typedArray = context.obtainStyledAttributes(attrs, R.styleable.FormEditText, 0, 0) + validatorParameters = DefaultEditTextValidator.Parameters() + validatorParameters.emptyAllowed = typedArray.getBoolean(R.styleable.FormEditText_emptyAllowed, false) + validatorParameters.testType = typedArray.getInt(R.styleable.FormEditText_testType, EditTextValidator.TEST_NOCHECK) + validatorParameters.testErrorString = typedArray.getString(R.styleable.FormEditText_testErrorString) + validatorParameters.classType = typedArray.getString(R.styleable.FormEditText_classType) + validatorParameters.customRegexp = typedArray.getString(R.styleable.FormEditText_customRegexp) + validatorParameters.emptyErrorStringDef = typedArray.getString(R.styleable.FormEditText_emptyErrorString) + validatorParameters.customFormat = typedArray.getString(R.styleable.FormEditText_customFormat) + if (validatorParameters.testType == EditTextValidator.TEST_MIN_LENGTH) + validatorParameters.minLength = typedArray.getInt(R.styleable.FormEditText_minLength, 0) + if (validatorParameters.testType == EditTextValidator.TEST_NUMERIC_RANGE) { + validatorParameters.minNumber = typedArray.getInt(R.styleable.FormEditText_minNumber, Int.MIN_VALUE) + validatorParameters.maxNumber = typedArray.getInt(R.styleable.FormEditText_maxNumber, Int.MAX_VALUE) + } + if (validatorParameters.testType == EditTextValidator.TEST_FLOAT_NUMERIC_RANGE) { + validatorParameters.floatminNumber = typedArray.getFloat(R.styleable.FormEditText_floatminNumber, Float.MIN_VALUE) + validatorParameters.floatmaxNumber = typedArray.getFloat(R.styleable.FormEditText_floatmaxNumber, Float.MAX_VALUE) + } + typedArray.recycle() + } } diff --git a/crowdin.yml b/crowdin.yml index 6069f66394..d12e37292f 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -19,5 +19,7 @@ files: translation: /dana/src/main/res/values-%android_code%/strings.xml - source: /medtronic/src/main/res/values/strings.xml translation: /medtronic/src/main/res/values-%android_code%/strings.xml + - source: /omnipod/src/main/res/values/strings.xml + translation: /omnipod/src/main/res/values-%android_code%/strings.xml - source: /rileylink/src/main/res/values/strings.xml translation: /rileylink/src/main/res/values-%android_code%/strings.xml diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/OmnipodPumpPlugin.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/OmnipodPumpPlugin.java index 2ff73c5add..0cbb4f3d34 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/OmnipodPumpPlugin.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/OmnipodPumpPlugin.java @@ -21,6 +21,8 @@ import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.function.Supplier; import javax.inject.Inject; @@ -73,14 +75,18 @@ import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodCommand import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodCustomActionType; import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodStatusRequestType; import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodStorageKeys; +import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.service.ExpirationReminderBuilder; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoRecentPulseLog; +import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertConfiguration; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodPumpValuesChanged; +import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodTbrChanged; import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsOmnipodManager; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.service.RileyLinkOmnipodService; import info.nightscout.androidaps.plugins.pump.omnipod.ui.OmnipodFragment; import info.nightscout.androidaps.plugins.pump.omnipod.util.AapsOmnipodUtil; +import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodAlertUtil; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; import info.nightscout.androidaps.utils.FabricPrivacy; @@ -107,6 +113,7 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface, private final AapsOmnipodManager aapsOmnipodManager; private final AapsOmnipodUtil aapsOmnipodUtil; private final RileyLinkUtil rileyLinkUtil; + private final OmnipodAlertUtil omnipodAlertUtil; private final AAPSLogger aapsLogger; private final RxBusWrapper rxBus; private final ActivePluginProvider activePlugin; @@ -154,7 +161,8 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface, ServiceTaskExecutor serviceTaskExecutor, DateUtil dateUtil, AapsOmnipodUtil aapsOmnipodUtil, - RileyLinkUtil rileyLinkUtil + RileyLinkUtil rileyLinkUtil, + OmnipodAlertUtil omnipodAlertUtil ) { super(new PluginDescription() // .mainType(PluginType.PUMP) // @@ -178,6 +186,7 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface, this.aapsOmnipodManager = aapsOmnipodManager; this.aapsOmnipodUtil = aapsOmnipodUtil; this.rileyLinkUtil = rileyLinkUtil; + this.omnipodAlertUtil = omnipodAlertUtil; pumpDescription = new PumpDescription(pumpType); @@ -220,9 +229,13 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface, aapsOmnipodManager.createSuspendedFakeTbrIfNotExists(); } - if (!OmnipodPumpPlugin.this.statusRequestList.isEmpty() || OmnipodPumpPlugin.this.hasTimeDateOrTimeZoneChanged) { - if (!getCommandQueue().statusInQueue()) { - getCommandQueue().readStatus(statusRequestList.isEmpty() ? "Date or Time Zone Changed" : "Status Refresh Requested", null); + if (!getCommandQueue().statusInQueue()) { + if (!OmnipodPumpPlugin.this.statusRequestList.isEmpty()) { + getCommandQueue().readStatus("Status Refresh Requested", null); + } else if (OmnipodPumpPlugin.this.hasTimeDateOrTimeZoneChanged) { + getCommandQueue().readStatus("Date or Time Zone Changed", null); + } else if (!OmnipodPumpPlugin.this.verifyPodAlertConfiguration()) { + getCommandQueue().readStatus("Expiration Alerts Changed", null); } } @@ -254,6 +267,11 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface, .observeOn(Schedulers.io()) .subscribe(event -> context.unbindService(serviceConnection), fabricPrivacy::logException) ); + disposables.add(rxBus + .toObservable(EventOmnipodTbrChanged.class) + .observeOn(Schedulers.io()) + .subscribe(event -> updateAapsTbr(), fabricPrivacy::logException) + ); disposables.add(rxBus .toObservable(EventPreferenceChange.class) .observeOn(Schedulers.io()) @@ -264,8 +282,16 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface, (event.isChanged(getResourceHelper(), R.string.key_omnipod_smb_beeps_enabled)) || (event.isChanged(getResourceHelper(), R.string.key_omnipod_suspend_delivery_button_enabled)) || (event.isChanged(getResourceHelper(), R.string.key_omnipod_pulse_log_button_enabled)) || - (event.isChanged(getResourceHelper(), R.string.key_omnipod_time_change_event_enabled))) + (event.isChanged(getResourceHelper(), R.string.key_omnipod_time_change_event_enabled))) { aapsOmnipodManager.reloadSettings(); + } else if (event.isChanged(getResourceHelper(), R.string.key_omnipod_expiration_reminder_enabled) || + event.isChanged(getResourceHelper(), R.string.key_omnipod_expiration_reminder_hours_before_shutdown) || + event.isChanged(getResourceHelper(), R.string.key_omnipod_low_reservoir_alert_enabled) || + event.isChanged(getResourceHelper(), R.string.key_omnipod_low_reservoir_alert_units)) { + if (!verifyPodAlertConfiguration() && !getCommandQueue().statusInQueue()) { + getCommandQueue().readStatus("Expiration Alerts Changed", null); + } + } }, fabricPrivacy::logException) ); disposables.add(rxBus @@ -291,6 +317,17 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface, ); } + private void updateAapsTbr() { + // As per the characteristics of the Omnipod, we only know whether or not a TBR is currently active + // But it doesn't tell us the duration or amount, so we can only update TBR status in AAPS if + // The pod is not running a TBR, while AAPS thinks it is + if (!podStateManager.isTempBasalRunning()) { + if (activePlugin.getActiveTreatments().isTempBasalInProgress() && !aapsOmnipodManager.hasSuspendedFakeTbr()) { + aapsOmnipodManager.reportCancelledTbr(); + } + } + } + @Override protected void onStop() { super.onStop(); @@ -473,6 +510,32 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface, timeChangeRetries = 0; } } + } else if (!verifyPodAlertConfiguration()) { + Duration expirationReminderTimeBeforeShutdown = omnipodAlertUtil.getExpirationReminderTimeBeforeShutdown(); + Integer lowReservoirAlertUnits = omnipodAlertUtil.getLowReservoirAlertUnits(); + + List alertConfigurations = new ExpirationReminderBuilder(podStateManager) // + .expirationAdvisory(expirationReminderTimeBeforeShutdown != null, + Optional.ofNullable(expirationReminderTimeBeforeShutdown).orElse(Duration.ZERO)) // + .lowReservoir(lowReservoirAlertUnits != null, Optional.ofNullable(lowReservoirAlertUnits).orElse(0)) // + .build(); + + PumpEnactResult result = executeCommand(OmnipodCommandType.CONFIGURE_ALERTS, () -> aapsOmnipodManager.configureAlerts(alertConfigurations)); + + if (result.success) { + aapsLogger.info(LTag.PUMP, "Successfully configured alerts in Pod"); + + podStateManager.setExpirationAlertTimeBeforeShutdown(expirationReminderTimeBeforeShutdown); + podStateManager.setLowReservoirAlertUnits(lowReservoirAlertUnits); + + Notification notification = new Notification( + Notification.OMNIPOD_POD_ALERTS_UPDATED, + resourceHelper.gs(R.string.omnipod_expiration_alerts_updated), + Notification.INFO, 60); + rxBus.send(new EventNewNotification(notification)); + } else { + aapsLogger.warn(LTag.PUMP, "Failed to configure alerts in Pod"); + } } } @@ -483,19 +546,6 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface, aapsLogger.info(LTag.PUMP, "Basal Profile was set: " + result.success); - if (result.success) { - rxBus.send(new EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED)); - Notification notification = new Notification(Notification.PROFILE_SET_OK, - resourceHelper.gs(R.string.profile_set_ok), - Notification.INFO, 60); - rxBus.send(new EventNewNotification(notification)); - } else { - Notification notification = new Notification(Notification.FAILED_UDPATE_PROFILE, - resourceHelper.gs(R.string.failedupdatebasalprofile), - Notification.URGENT); - rxBus.send(new EventNewNotification(notification)); - } - return result; } @@ -890,7 +940,6 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface, T pumpEnactResult = supplier.get(); - // TODO maybe only do this for specific commands rxBus.send(new EventRefreshOverview("Omnipod command: " + commandType.name(), false)); rxBus.send(new EventOmnipodPumpValuesChanged()); @@ -898,6 +947,22 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface, return pumpEnactResult; } + private boolean verifyPodAlertConfiguration() { + if (podStateManager.isPodRunning()) { + Duration expirationReminderHoursBeforeShutdown = omnipodAlertUtil.getExpirationReminderTimeBeforeShutdown(); + Integer lowReservoirAlertUnits = omnipodAlertUtil.getLowReservoirAlertUnits(); + + if (!Objects.equals(expirationReminderHoursBeforeShutdown, podStateManager.getExpirationAlertTimeBeforeShutdown()) + || !Objects.equals(lowReservoirAlertUnits, podStateManager.getLowReservoirAlertUnits())) { + aapsLogger.warn(LTag.PUMP, "Configured alerts in Pod don't match AAPS settings: expirationReminderHoursBeforeShutdown = {} (AAPS) vs {} Pod, " + + "lowReservoirAlertUnits = {} (AAPS) vs {} (Pod)", expirationReminderHoursBeforeShutdown, podStateManager.getExpirationAlertTimeBeforeShutdown(), + lowReservoirAlertUnits, podStateManager.getLowReservoirAlertUnits()); + return false; + } + } + return true; + } + private void incrementStatistics(String statsKey) { long currentCount = sp.getLong(statsKey, 0L); currentCount++; diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dagger/OmnipodModule.kt b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dagger/OmnipodModule.kt new file mode 100644 index 0000000000..770de8d277 --- /dev/null +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dagger/OmnipodModule.kt @@ -0,0 +1,50 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.dagger + +import dagger.Module +import dagger.Provides +import dagger.android.ContributesAndroidInjector +import info.nightscout.androidaps.plugins.pump.omnipod.OmnipodPumpPlugin +import info.nightscout.androidaps.plugins.pump.omnipod.data.RLHistoryItemOmnipod +import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager +import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsOmnipodManager +import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsPodStateManager +import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager +import info.nightscout.androidaps.plugins.pump.omnipod.ui.PodHistoryActivity +import info.nightscout.androidaps.plugins.pump.omnipod.ui.PodManagementActivity +import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.initpod.InitActionFragment +import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.initpod.InitPodTask +import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.pages.InitPodRefreshAction +import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.pages.PodInfoFragment +import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.removepod.RemoveActionFragment + +@Module +@Suppress("unused") +abstract class OmnipodModule { + + // Activities + @ContributesAndroidInjector + abstract fun contributesPodManagementActivity(): PodManagementActivity + @ContributesAndroidInjector abstract fun contributesPodHistoryActivity(): PodHistoryActivity + + // Fragments + @ContributesAndroidInjector abstract fun initActionFragment(): InitActionFragment + @ContributesAndroidInjector abstract fun removeActionFragment(): RemoveActionFragment + @ContributesAndroidInjector abstract fun podInfoFragment(): PodInfoFragment + + // Service + @ContributesAndroidInjector + abstract fun omnipodCommunicationManagerProvider(): OmnipodRileyLinkCommunicationManager + @ContributesAndroidInjector abstract fun aapsOmnipodManagerProvider(): AapsOmnipodManager + + // Data + @ContributesAndroidInjector abstract fun initPodRefreshAction(): InitPodRefreshAction + @ContributesAndroidInjector abstract fun podStateManager(): PodStateManager + @ContributesAndroidInjector abstract fun initPodTask(): InitPodTask + @ContributesAndroidInjector abstract fun omnipodPumpPlugin(): OmnipodPumpPlugin + @ContributesAndroidInjector abstract fun rlHistoryItemOmnipod(): RLHistoryItemOmnipod + + companion object { + @Provides + fun podStateManagerProvider(aapsPodStateManager: AapsPodStateManager): PodStateManager = aapsPodStateManager + } +} diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/definition/OmnipodCommandType.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/definition/OmnipodCommandType.java index 5bb6bcdb2e..b7be57c864 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/definition/OmnipodCommandType.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/definition/OmnipodCommandType.java @@ -17,6 +17,7 @@ public enum OmnipodCommandType { DISCARD_POD(R.string.omnipod_cmd_discard_pod), // GET_POD_STATUS(R.string.omnipod_cmd_get_pod_status), // SET_TIME(R.string.omnipod_cmd_set_time), // + CONFIGURE_ALERTS(R.string.omnipod_cmd_configure_alerts), // ACKNOWLEDGE_ALERTS(R.string.omnipod_cmd_acknowledge_alerts), // GET_POD_PULSE_LOG(R.string.omnipod_cmd_get_pulse_log), // SUSPEND_DELIVERY(R.string.omnipod_cmd_suspend_delivery); diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/definition/OmnipodStorageKeys.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/definition/OmnipodStorageKeys.java index 7a7ce00fae..272325ea90 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/definition/OmnipodStorageKeys.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/definition/OmnipodStorageKeys.java @@ -15,6 +15,10 @@ public class OmnipodStorageKeys { public static final int SUSPEND_DELIVERY_BUTTON_ENABLED = R.string.key_omnipod_pulse_log_button_enabled; public static final int PULSE_LOG_BUTTON_ENABLED = R.string.key_omnipod_pulse_log_button_enabled; public static final int TIME_CHANGE_EVENT_ENABLED = R.string.key_omnipod_time_change_event_enabled; + public static final int EXPIRATION_REMINDER_ENABLED = R.string.key_omnipod_expiration_reminder_enabled; + public static final int EXPIRATION_REMINDER_HOURS_BEFORE_SHUTDOWN = R.string.key_omnipod_expiration_reminder_hours_before_shutdown; + public static final int LOW_RESERVOIR_ALERT_ENABLED = R.string.key_omnipod_low_reservoir_alert_enabled; + public static final int LOW_RESERVOIR_ALERT_UNITS = R.string.key_omnipod_low_reservoir_alert_units; } public static class Statistics { diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/definition/PodHistoryEntryType.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/definition/PodHistoryEntryType.java index d7b57932e1..cd15bac1a4 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/definition/PodHistoryEntryType.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/definition/PodHistoryEntryType.java @@ -21,8 +21,8 @@ public enum PodHistoryEntryType { SET_TEMPORARY_BASAL(10, R.string.omnipod_cmd_set_tbr, PumpHistoryEntryGroup.Basal), CANCEL_TEMPORARY_BASAL_BY_DRIVER(11, R.string.omnipod_cmd_cancel_tbr_by_driver, PumpHistoryEntryGroup.Basal), CANCEL_TEMPORARY_BASAL(12, R.string.omnipod_cmd_cancel_tbr, PumpHistoryEntryGroup.Basal), - SET_FAKE_SUSPENDED_TEMPORARY_BASAL(13, R.string.omnipod_cmd_set_fake_suspended_tbr), - CANCEL_FAKE_SUSPENDED_TEMPORARY_BASAL(14, R.string.omnipod_cmd_cancel_fake_suspended_tbr), + SET_FAKE_SUSPENDED_TEMPORARY_BASAL(13, R.string.omnipod_cmd_set_fake_suspended_tbr, PumpHistoryEntryGroup.Basal), + CANCEL_FAKE_SUSPENDED_TEMPORARY_BASAL(14, R.string.omnipod_cmd_cancel_fake_suspended_tbr, PumpHistoryEntryGroup.Basal), SET_BASAL_SCHEDULE(20, R.string.omnipod_cmd_set_basal_schedule, PumpHistoryEntryGroup.Basal), diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/ConfigureAlertsAction.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/ConfigureAlertsAction.java index 473e7de6bc..bc4235c2e0 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/ConfigureAlertsAction.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/ConfigureAlertsAction.java @@ -28,9 +28,17 @@ public class ConfigureAlertsAction implements OmnipodAction { public StatusResponse execute(OmnipodRileyLinkCommunicationManager communicationService) { ConfigureAlertsCommand configureAlertsCommand = new ConfigureAlertsCommand(podStateManager.getCurrentNonce(), alertConfigurations); StatusResponse statusResponse = communicationService.sendCommand(StatusResponse.class, podStateManager, configureAlertsCommand); - for (AlertConfiguration alertConfiguration : alertConfigurations) { - podStateManager.putConfiguredAlert(alertConfiguration.getAlertSlot(), alertConfiguration.getAlertType()); - } + updateConfiguredAlerts(podStateManager, alertConfigurations); return statusResponse; } + + public static void updateConfiguredAlerts(PodStateManager podStateManager, List alertConfigurations) { + for (AlertConfiguration alertConfiguration : alertConfigurations) { + if (alertConfiguration.isActive()) { + podStateManager.putConfiguredAlert(alertConfiguration.getAlertSlot(), alertConfiguration.getAlertType()); + } else { + podStateManager.removeConfiguredAlert(alertConfiguration.getAlertSlot()); + } + } + } } diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/InsertCannulaAction.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/InsertCannulaAction.java index cae261d7ed..b364aa8c81 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/InsertCannulaAction.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/InsertCannulaAction.java @@ -1,7 +1,14 @@ package info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action; -import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.service.InsertCannulaService; +import org.joda.time.Duration; + +import java.util.List; +import java.util.Optional; + +import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.service.ExpirationReminderBuilder; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; +import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertConfiguration; +import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BasalSchedule; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActionInitializationException; @@ -12,22 +19,22 @@ import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.Omnipod public class InsertCannulaAction implements OmnipodAction { private final PodStateManager podStateManager; - private final InsertCannulaService service; private final BasalSchedule initialBasalSchedule; + private final Duration expirationReminderTimeBeforeShutdown; + private final Integer lowReservoirAlertUnits; - public InsertCannulaAction(InsertCannulaService insertCannulaService, PodStateManager podStateManager, BasalSchedule initialBasalSchedule) { - if (insertCannulaService == null) { - throw new ActionInitializationException("Insert cannula service cannot be null"); - } + public InsertCannulaAction(PodStateManager podStateManager, BasalSchedule initialBasalSchedule, + Duration expirationReminderTimeBeforeShutdown, Integer lowReservoirAlertUnits) { if (podStateManager == null) { throw new ActionInitializationException("Pod state manager cannot be null"); } if (initialBasalSchedule == null) { throw new ActionInitializationException("Initial basal schedule cannot be null"); } - this.service = insertCannulaService; this.podStateManager = podStateManager; this.initialBasalSchedule = initialBasalSchedule; + this.expirationReminderTimeBeforeShutdown = expirationReminderTimeBeforeShutdown; + this.lowReservoirAlertUnits = lowReservoirAlertUnits; } @Override @@ -37,12 +44,19 @@ public class InsertCannulaAction implements OmnipodAction { } if (podStateManager.getPodProgressStatus().isBefore(PodProgressStatus.BASAL_INITIALIZED)) { - service.programInitialBasalSchedule(communicationService, podStateManager, initialBasalSchedule); + podStateManager.setBasalSchedule(initialBasalSchedule); + communicationService.executeAction(new SetBasalScheduleAction(podStateManager, initialBasalSchedule, + true, podStateManager.getScheduleOffset(), false)); } if (podStateManager.getPodProgressStatus().isBefore(PodProgressStatus.INSERTING_CANNULA)) { - service.executeExpirationRemindersAlertCommand(communicationService, podStateManager); - return service.executeInsertionBolusCommand(communicationService, podStateManager); + communicationService.executeAction(new ConfigureAlertsAction(podStateManager, buildAlertConfigurations())); + + podStateManager.setExpirationAlertTimeBeforeShutdown(expirationReminderTimeBeforeShutdown); + podStateManager.setLowReservoirAlertUnits(lowReservoirAlertUnits); + + return communicationService.executeAction(new BolusAction(podStateManager, OmnipodConstants.POD_CANNULA_INSERTION_BOLUS_UNITS, + Duration.standardSeconds(1), false, false)); } else if (podStateManager.getPodProgressStatus().equals(PodProgressStatus.INSERTING_CANNULA)) { // Check status return communicationService.executeAction(new GetStatusAction(podStateManager)); @@ -50,4 +64,12 @@ public class InsertCannulaAction implements OmnipodAction { throw new IllegalPodProgressException(null, podStateManager.getPodProgressStatus()); } } + + private List buildAlertConfigurations() { + ExpirationReminderBuilder builder = new ExpirationReminderBuilder(podStateManager).defaults(); + builder.expirationAdvisory(expirationReminderTimeBeforeShutdown != null, + Optional.ofNullable(expirationReminderTimeBeforeShutdown).orElse(Duration.ZERO)); + builder.lowReservoir(lowReservoirAlertUnits != null, Optional.ofNullable(lowReservoirAlertUnits).orElse(0)); + return builder.build(); + } } diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/SetBasalScheduleAction.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/SetBasalScheduleAction.java index c3471e3bdf..a64766c5d3 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/SetBasalScheduleAction.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/SetBasalScheduleAction.java @@ -46,8 +46,6 @@ public class SetBasalScheduleAction implements OmnipodAction { OmnipodMessage basalMessage = new OmnipodMessage(podStateManager.getAddress(), Arrays.asList(setBasal, extraCommand), podStateManager.getMessageNumber()); - StatusResponse statusResponse = communicationService.exchangeMessages(StatusResponse.class, podStateManager, basalMessage); - podStateManager.setBasalSchedule(basalSchedule); - return statusResponse; + return communicationService.exchangeMessages(StatusResponse.class, podStateManager, basalMessage); } } diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/SetTempBasalAction.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/SetTempBasalAction.java index 425001a595..cadb602f68 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/SetTempBasalAction.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/SetTempBasalAction.java @@ -1,6 +1,5 @@ package info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action; -import org.joda.time.DateTime; import org.joda.time.Duration; import java.util.Arrays; @@ -11,7 +10,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.mess import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.SetInsulinScheduleCommand; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.TempBasalExtraCommand; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; -import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActionInitializationException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; @@ -45,8 +43,6 @@ public class SetTempBasalAction implements OmnipodAction { new TempBasalExtraCommand(rate, duration, acknowledgementBeep, completionBeep, Duration.ZERO)); OmnipodMessage message = new OmnipodMessage(podStateManager.getAddress(), messageBlocks, podStateManager.getMessageNumber()); - StatusResponse statusResponse = communicationService.exchangeMessages(StatusResponse.class, podStateManager, message); - podStateManager.setLastTempBasal(DateTime.now().minus(OmnipodConstants.AVERAGE_TEMP_BASAL_COMMAND_COMMUNICATION_DURATION), rate, duration); - return statusResponse; + return communicationService.exchangeMessages(StatusResponse.class, podStateManager, message); } } diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/service/ExpirationReminderBuilder.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/service/ExpirationReminderBuilder.java new file mode 100644 index 0000000000..990bed5de6 --- /dev/null +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/service/ExpirationReminderBuilder.java @@ -0,0 +1,69 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.service; + +import org.joda.time.DateTime; +import org.joda.time.Duration; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertConfiguration; +import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertSlot; +import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants; +import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; +import info.nightscout.androidaps.plugins.pump.omnipod.driver.util.AlertConfigurationUtil; + +public final class ExpirationReminderBuilder { + private final Map alerts = new HashMap<>(); + private final DateTime endOfServiceTime; + private final PodStateManager podStateManager; + + public ExpirationReminderBuilder(PodStateManager podStateManager) { + this.endOfServiceTime = podStateManager.getActivatedAt().plus(OmnipodConstants.SERVICE_DURATION); + this.podStateManager = podStateManager; + } + + public ExpirationReminderBuilder defaults() { + DateTime shutdownImminentAlarmTime = endOfServiceTime.minus(OmnipodConstants.END_OF_SERVICE_IMMINENT_WINDOW); + + if (DateTime.now().isBefore(shutdownImminentAlarmTime)) { + Duration timeUntilShutdownImminentAlarm = new Duration(DateTime.now(), + shutdownImminentAlarmTime); + AlertConfiguration shutdownImminentAlertConfiguration = AlertConfigurationUtil.createShutdownImminentAlertConfiguration( + timeUntilShutdownImminentAlarm); + alerts.put(shutdownImminentAlertConfiguration.getAlertSlot(), shutdownImminentAlertConfiguration); + } + + AlertConfiguration autoOffAlertConfiguration = AlertConfigurationUtil.createAutoOffAlertConfiguration( + false, Duration.ZERO); + alerts.put(autoOffAlertConfiguration.getAlertSlot(), autoOffAlertConfiguration); + + return this; + } + + public ExpirationReminderBuilder expirationAdvisory(boolean active, Duration timeBeforeShutdown) { + DateTime expirationAdvisoryAlarmTime = endOfServiceTime.minus(timeBeforeShutdown); + + if (DateTime.now().isBefore(expirationAdvisoryAlarmTime)) { + Duration timeUntilExpirationAdvisoryAlarm = new Duration(DateTime.now(), + expirationAdvisoryAlarmTime); + AlertConfiguration expirationAdvisoryAlertConfiguration = AlertConfigurationUtil.createExpirationAdvisoryAlertConfiguration(active, + timeUntilExpirationAdvisoryAlarm, timeBeforeShutdown); + alerts.put(expirationAdvisoryAlertConfiguration.getAlertSlot(), expirationAdvisoryAlertConfiguration); + } + return this; + } + + public ExpirationReminderBuilder lowReservoir(boolean active, int units) { + if (podStateManager.getReservoirLevel() == null || podStateManager.getReservoirLevel().intValue() > units) { + AlertConfiguration lowReservoirAlertConfiguration = AlertConfigurationUtil.createLowReservoirAlertConfiguration(active, (double) units); + alerts.put(lowReservoirAlertConfiguration.getAlertSlot(), lowReservoirAlertConfiguration); + } + return this; + } + + public List build() { + return new ArrayList<>(alerts.values()); + } +} diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/service/InsertCannulaService.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/service/InsertCannulaService.java deleted file mode 100644 index 756fe9c76c..0000000000 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/service/InsertCannulaService.java +++ /dev/null @@ -1,59 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.service; - -import org.joda.time.DateTime; -import org.joda.time.Duration; - -import java.util.Arrays; -import java.util.List; - -import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.BolusAction; -import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.ConfigureAlertsAction; -import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.SetBasalScheduleAction; -import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; -import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertConfiguration; -import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants; -import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BasalSchedule; -import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; -import info.nightscout.androidaps.plugins.pump.omnipod.driver.util.AlertConfigurationUtil; -import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; - -public class InsertCannulaService { - public StatusResponse programInitialBasalSchedule(OmnipodRileyLinkCommunicationManager communicationService, - PodStateManager podStateManager, BasalSchedule basalSchedule) { - return communicationService.executeAction(new SetBasalScheduleAction(podStateManager, basalSchedule, - true, podStateManager.getScheduleOffset(), false)); - } - - public StatusResponse executeExpirationRemindersAlertCommand(OmnipodRileyLinkCommunicationManager communicationService, - PodStateManager podStateManager) { - AlertConfiguration lowReservoirAlertConfiguration = AlertConfigurationUtil.createLowReservoirAlertConfiguration(OmnipodConstants.LOW_RESERVOIR_ALERT); - - DateTime endOfServiceTime = podStateManager.getActivatedAt().plus(OmnipodConstants.SERVICE_DURATION); - - Duration timeUntilExpirationAdvisoryAlarm = new Duration(DateTime.now(), - endOfServiceTime.minus(OmnipodConstants.EXPIRATION_ADVISORY_WINDOW)); - Duration timeUntilShutdownImminentAlarm = new Duration(DateTime.now(), - endOfServiceTime.minus(OmnipodConstants.END_OF_SERVICE_IMMINENT_WINDOW)); - - AlertConfiguration expirationAdvisoryAlertConfiguration = AlertConfigurationUtil.createExpirationAdvisoryAlertConfiguration( - timeUntilExpirationAdvisoryAlarm, OmnipodConstants.EXPIRATION_ADVISORY_WINDOW); - AlertConfiguration shutdownImminentAlertConfiguration = AlertConfigurationUtil.createShutdownImminentAlertConfiguration( - timeUntilShutdownImminentAlarm); - AlertConfiguration autoOffAlertConfiguration = AlertConfigurationUtil.createAutoOffAlertConfiguration( - false, Duration.ZERO); - - List alertConfigurations = Arrays.asList( // - lowReservoirAlertConfiguration, // - expirationAdvisoryAlertConfiguration, // - shutdownImminentAlertConfiguration, // - autoOffAlertConfiguration // - ); - - return communicationService.executeAction(new ConfigureAlertsAction(podStateManager, alertConfigurations)); - } - - public StatusResponse executeInsertionBolusCommand(OmnipodRileyLinkCommunicationManager communicationService, PodStateManager podStateManager) { - return communicationService.executeAction(new BolusAction(podStateManager, OmnipodConstants.POD_CANNULA_INSERTION_BOLUS_UNITS, - Duration.standardSeconds(1), false, false)); - } -} diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/message/response/podinfo/PodInfoResponse.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/message/response/podinfo/PodInfoResponse.java index c8e178c6cd..cee243c45f 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/message/response/podinfo/PodInfoResponse.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/message/response/podinfo/PodInfoResponse.java @@ -21,8 +21,8 @@ public class PodInfoResponse extends MessageBlock { return subType; } - public T getPodInfo() { - return (T) podInfo; + public PodInfo getPodInfo() { + return podInfo; } @Override diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/definition/AlertConfiguration.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/definition/AlertConfiguration.java index 4703dccb5a..ab72fe323f 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/definition/AlertConfiguration.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/definition/AlertConfiguration.java @@ -15,8 +15,7 @@ public class AlertConfiguration { private final BeepType beepType; public AlertConfiguration(AlertType alertType, AlertSlot alertSlot, boolean active, boolean autoOffModifier, - Duration duration, AlertTrigger alertTrigger, - BeepType beepType, BeepRepeat beepRepeat) { + Duration duration, AlertTrigger alertTrigger, BeepType beepType, BeepRepeat beepRepeat) { this.alertType = alertType; this.alertSlot = alertSlot; this.active = active; @@ -35,6 +34,14 @@ public class AlertConfiguration { return alertSlot; } + public AlertTrigger getAlertTrigger() { + return alertTrigger; + } + + public boolean isActive() { + return active; + } + public byte[] getRawData() { int firstByte = (alertSlot.getValue() << 4); firstByte += active ? (1 << 3) : 0; @@ -67,4 +74,17 @@ public class AlertConfiguration { return encodedData; } + + @Override public String toString() { + return "AlertConfiguration{" + + "alertType=" + alertType + + ", alertSlot=" + alertSlot + + ", active=" + active + + ", autoOffModifier=" + autoOffModifier + + ", duration=" + duration + + ", alertTrigger=" + alertTrigger + + ", beepRepeat=" + beepRepeat + + ", beepType=" + beepType + + '}'; + } } diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/definition/AlertTrigger.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/definition/AlertTrigger.java index d2835388f7..533c465919 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/definition/AlertTrigger.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/definition/AlertTrigger.java @@ -1,6 +1,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.driver.definition; -abstract class AlertTrigger { +public abstract class AlertTrigger { private final T value; AlertTrigger(T value) { @@ -10,4 +10,10 @@ abstract class AlertTrigger { public T getValue() { return value; } + + @Override public String toString() { + return "AlertTrigger{" + + "value=" + value + + '}'; + } } diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/manager/OmnipodManager.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/manager/OmnipodManager.java index 3d4928e633..9da707a26b 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/manager/OmnipodManager.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/manager/OmnipodManager.java @@ -5,6 +5,7 @@ import org.joda.time.DateTimeZone; import org.joda.time.Duration; import java.util.EnumSet; +import java.util.List; import java.util.TimeZone; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; @@ -16,6 +17,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.acti import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.AssignAddressAction; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.BolusAction; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.CancelDeliveryAction; +import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.ConfigureAlertsAction; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.DeactivatePodAction; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.GetPodInfoAction; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.GetStatusAction; @@ -24,12 +26,12 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.acti import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.SetBasalScheduleAction; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.SetTempBasalAction; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.SetupPodAction; -import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.service.InsertCannulaService; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.service.PrimeService; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.CancelDeliveryCommand; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoRecentPulseLog; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoResponse; +import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertConfiguration; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.BeepType; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.DeliveryStatus; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.DeliveryType; @@ -131,7 +133,8 @@ public class OmnipodManager { .observeOn(Schedulers.io()); } - public synchronized Single insertCannula(BasalSchedule basalSchedule) { + public synchronized Single insertCannula( + BasalSchedule basalSchedule, Duration expirationReminderTimeBeforeShutdown, Integer lowReservoirAlertUnits) { if (!podStateManager.isPodInitialized() || podStateManager.getPodProgressStatus().isBefore(PodProgressStatus.PRIMING_COMPLETED)) { throw new IllegalPodProgressException(PodProgressStatus.PRIMING_COMPLETED, !podStateManager.isPodInitialized() ? null : podStateManager.getPodProgressStatus()); } @@ -146,7 +149,7 @@ public class OmnipodManager { logStartingCommandExecution("insertCannula [basalSchedule=" + basalSchedule + "]"); try { - communicationService.executeAction(new InsertCannulaAction(new InsertCannulaService(), podStateManager, basalSchedule)); + communicationService.executeAction(new InsertCannulaAction(podStateManager, basalSchedule, expirationReminderTimeBeforeShutdown, lowReservoirAlertUnits)); } finally { logCommandExecutionFinished("insertCannula"); } @@ -184,6 +187,18 @@ public class OmnipodManager { } } + public synchronized StatusResponse configureAlerts(List alertConfigurations) { + assertReadyForDelivery(); + logStartingCommandExecution("configureAlerts"); + try { + StatusResponse statusResponse = executeAndVerify(() -> communicationService.executeAction(new ConfigureAlertsAction(podStateManager, alertConfigurations))); + ConfigureAlertsAction.updateConfiguredAlerts(podStateManager, alertConfigurations); + return statusResponse; + } finally { + logCommandExecutionFinished("configureAlerts"); + } + } + public synchronized StatusResponse acknowledgeAlerts() { assertReadyForDelivery(); @@ -209,6 +224,10 @@ public class OmnipodManager { suspendDelivery(acknowledgementBeep); } + // Store the new Basal schedule after successfully suspending delivery, so that if setting the Basal schedule fails, + // And we later try to resume delivery, the new schedule is used + podStateManager.setBasalSchedule(schedule); + try { executeAndVerify(() -> communicationService.executeAction(new SetBasalScheduleAction(podStateManager, schedule, false, podStateManager.getScheduleOffset(), acknowledgementBeep))); @@ -240,7 +259,7 @@ public class OmnipodManager { logStartingCommandExecution("setTemporaryBasal [rate=" + rate + ", duration=" + duration + ", acknowledgementBeep=" + acknowledgementBeep + ", completionBeep=" + completionBeep + "]"); - boolean cancelCurrentTbr = podStateManager.getLastDeliveryStatus().isTbrRunning(); + boolean cancelCurrentTbr = podStateManager.isTempBasalRunning(); try { if (cancelCurrentTbr) { @@ -262,6 +281,7 @@ public class OmnipodManager { try { executeAndVerify(() -> communicationService.executeAction(new SetTempBasalAction( podStateManager, rate, duration, acknowledgementBeep, completionBeep))); + podStateManager.setTempBasal(DateTime.now().minus(OmnipodConstants.AVERAGE_TEMP_BASAL_COMMAND_COMMUNICATION_DURATION), rate, duration, true); } catch (OmnipodException ex) { if (ex.isCertainFailure()) { if (cancelCurrentTbr) { @@ -271,13 +291,28 @@ public class OmnipodManager { } // verifyDeliveryStatus will throw an exception if verification fails - if (!verifyDeliveryStatus(DeliveryStatus.TEMP_BASAL_RUNNING, ex)) { - if (cancelCurrentTbr) { - throw new CommandFailedAfterChangingDeliveryStatusException("Failed to set new TBR while cancelling old TBR succeeded", ex); - } + try { + if (verifyDeliveryStatus(DeliveryStatus.TEMP_BASAL_RUNNING, ex)) { + podStateManager.setTempBasal(DateTime.now().minus(OmnipodConstants.AVERAGE_TEMP_BASAL_COMMAND_COMMUNICATION_DURATION), rate, duration, true); + } else { + if (cancelCurrentTbr) { + throw new CommandFailedAfterChangingDeliveryStatusException("Failed to set new TBR while cancelling old TBR succeeded", ex); + } - ex.setCertainFailure(true); - throw ex; + ex.setCertainFailure(true); + throw ex; + } + } catch (CommandFailedAfterChangingDeliveryStatusException ex2) { + // Don't set temp basal in Pod State for this Exception + throw ex2; + } catch (OmnipodException ex2) { + if (!ex2.isCertainFailure()) { + // We're not sure that setting the new TBR failed, so we assume that it succeeded + // If it didn't, PodStateManager.updateFromResponse() will fix the state + // upon receiving the next StatusResponse + podStateManager.setTempBasal(DateTime.now().minus(OmnipodConstants.AVERAGE_TEMP_BASAL_COMMAND_COMMUNICATION_DURATION), rate, duration, false); + } + throw ex2; } } } finally { @@ -331,14 +366,16 @@ public class OmnipodManager { commandDeliveryStatus = CommandDeliveryStatus.UNCERTAIN_FAILURE; } - DateTime startDate = DateTime.now().minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION); - podStateManager.setLastBolus(startDate, units); + DateTime estimatedBolusStartDate = DateTime.now().minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION); + Duration estimatedBolusDuration = calculateBolusDuration(units, OmnipodConstants.POD_BOLUS_DELIVERY_RATE); + Duration estimatedRemainingBolusDuration = estimatedBolusDuration.minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION); + + podStateManager.setLastBolus(estimatedBolusStartDate, units, estimatedBolusDuration, commandDeliveryStatus == CommandDeliveryStatus.SUCCESS); CompositeDisposable disposables = new CompositeDisposable(); - Duration bolusDuration = calculateBolusDuration(units, OmnipodConstants.POD_BOLUS_DELIVERY_RATE); - Duration estimatedRemainingBolusDuration = bolusDuration.minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION); if (progressIndicationConsumer != null) { + int numberOfProgressReports = Math.max(20, Math.min(100, (int) Math.ceil(units) * 10)); long progressReportInterval = estimatedRemainingBolusDuration.getMillis() / numberOfProgressReports; @@ -354,7 +391,7 @@ public class OmnipodManager { SingleSubject bolusCompletionSubject = SingleSubject.create(); synchronized (bolusDataMutex) { - activeBolusData = new ActiveBolusData(units, startDate, bolusCompletionSubject, disposables); + activeBolusData = new ActiveBolusData(units, estimatedBolusStartDate, commandDeliveryStatus, bolusCompletionSubject, disposables); } // Return successful command execution AFTER storing activeBolusData @@ -379,7 +416,7 @@ public class OmnipodManager { break; } } catch (PodFaultException ex) { - // Substract units not delivered in case of a Pod failure + // Subtract units not delivered in case of a Pod failure bolusNotDelivered = ex.getFaultEvent().getBolusNotDelivered(); aapsLogger.debug(LTag.PUMPCOMM, "Caught PodFaultException in bolus completion verification", ex); @@ -427,7 +464,7 @@ public class OmnipodManager { private void discardActiveBolusData(double bolusNotDelivered) { synchronized (bolusDataMutex) { double unitsDelivered = activeBolusData.getUnits() - bolusNotDelivered; - podStateManager.setLastBolus(activeBolusData.getStartDate(), unitsDelivered); + podStateManager.setLastBolus(activeBolusData.getStartDate(), unitsDelivered, new Duration(activeBolusData.getStartDate(), DateTime.now()), activeBolusData.getCommandDeliveryStatus() == CommandDeliveryStatus.SUCCESS); activeBolusData.getDisposables().dispose(); activeBolusData.getBolusCompletionSubject().onSuccess(new BolusDeliveryResult(unitsDelivered)); activeBolusData = null; @@ -464,8 +501,6 @@ public class OmnipodManager { logStartingCommandExecution("setTime [acknowledgementBeeps=" + acknowledgementBeeps + "]"); try { - suspendDelivery(acknowledgementBeeps); - DateTimeZone oldTimeZone = podStateManager.getTimeZone(); try { @@ -475,20 +510,8 @@ public class OmnipodManager { setBasalSchedule(podStateManager.getBasalSchedule(), acknowledgementBeeps); } catch (OmnipodException ex) { - if (ex.isCertainFailure()) { - podStateManager.setTimeZone(oldTimeZone); - throw new CommandFailedAfterChangingDeliveryStatusException("Suspending delivery succeeded but resuming did not", ex); - } - - try { - // verifyDeliveryStatus will throw an exception if verification fails - if (!verifyDeliveryStatus(DeliveryStatus.NORMAL, ex)) { - throw new CommandFailedAfterChangingDeliveryStatusException("Suspending delivery succeeded but resuming did not", ex); - } - } catch (Exception ex2) { - podStateManager.setTimeZone(oldTimeZone); - throw ex2; - } + podStateManager.setTimeZone(oldTimeZone); + throw ex; } } finally { logCommandExecutionFinished("setTime"); @@ -505,7 +528,7 @@ public class OmnipodManager { // Try to get pulse log for diagnostics try { PodInfoResponse podInfoResponse = communicationService.executeAction(new GetPodInfoAction(podStateManager, PodInfoType.RECENT_PULSE_LOG)); - PodInfoRecentPulseLog pulseLogInfo = podInfoResponse.getPodInfo(); + PodInfoRecentPulseLog pulseLogInfo = (PodInfoRecentPulseLog) podInfoResponse.getPodInfo(); aapsLogger.info(LTag.PUMPCOMM, "Retrieved pulse log from the pod: {}", pulseLogInfo.toString()); } catch (Exception ex) { aapsLogger.warn(LTag.PUMPCOMM, "Failed to retrieve pulse log from the pod", ex); @@ -619,13 +642,14 @@ public class OmnipodManager { */ private boolean verifyDeliveryStatus(DeliveryStatus expectedStatus, Throwable verificationCause) { aapsLogger.debug(LTag.PUMPCOMM, "Attempting to verify delivery status (expected={})", expectedStatus); - for (int i = 0; 2 > i; i++) { + for (int i = 0; 3 > i; i++) { try { StatusResponse podStatus = getPodStatus(); aapsLogger.debug(LTag.PUMPCOMM, "Resolved delivery status (expected={}, actual={})", expectedStatus, podStatus.getDeliveryStatus()); return podStatus.getDeliveryStatus().equals(expectedStatus); - } catch (Exception ignored) { - // ignore and try to continue + } catch (Exception ex) { + aapsLogger.debug(LTag.PUMPCOMM, "Ignoring exception thrown in getPodStatus() during attempt to verify delivery status: {}: {}", + ex.getClass().getSimpleName(), ex.getMessage()); } } aapsLogger.warn(LTag.PUMPCOMM, "Failed to verify delivery status"); @@ -693,12 +717,14 @@ public class OmnipodManager { private static class ActiveBolusData { private final double units; private final DateTime startDate; + private final CommandDeliveryStatus commandDeliveryStatus; private final SingleSubject bolusCompletionSubject; private final CompositeDisposable disposables; - private ActiveBolusData(double units, DateTime startDate, SingleSubject bolusCompletionSubject, CompositeDisposable disposables) { + private ActiveBolusData(double units, DateTime startDate, CommandDeliveryStatus commandDeliveryStatus, SingleSubject bolusCompletionSubject, CompositeDisposable disposables) { this.units = units; this.startDate = startDate; + this.commandDeliveryStatus = commandDeliveryStatus; this.bolusCompletionSubject = bolusCompletionSubject; this.disposables = disposables; } @@ -711,6 +737,10 @@ public class OmnipodManager { return startDate; } + CommandDeliveryStatus getCommandDeliveryStatus() { + return commandDeliveryStatus; + } + CompositeDisposable getDisposables() { return disposables; } diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/manager/PodStateManager.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/manager/PodStateManager.java index 51cf0eaf31..4aed1057f6 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/manager/PodStateManager.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/manager/PodStateManager.java @@ -15,6 +15,7 @@ import org.joda.time.format.ISODateTimeFormat; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.function.Supplier; import info.nightscout.androidaps.logging.AAPSLogger; @@ -352,37 +353,110 @@ public abstract class PodStateManager { return getSafe(() -> podState.getLastBolusAmount()); } - public final void setLastBolus(DateTime startTime, double amount) { + public final Duration getLastBolusDuration() { + return getSafe(() -> podState.getLastBolusDuration()); + } + + public final boolean isLastBolusCertain() { + Boolean certain = getSafe(() -> podState.isLastBolusCertain()); + return certain == null || certain; + } + + public final void setLastBolus(DateTime startTime, double amount, Duration duration, boolean certain) { setAndStore(() -> { podState.setLastBolusStartTime(startTime); podState.setLastBolusAmount(amount); + podState.setLastBolusDuration(duration); + podState.setLastBolusCertain(certain); }); } - public final DateTime getLastTempBasalStartTime() { - return getSafe(() -> podState.getLastTempBasalStartTime()); + public final boolean hasLastBolus() { + return getLastBolusAmount() != null && getLastBolusDuration() != null && getLastBolusStartTime() != null; } - public final Double getLastTempBasalAmount() { - return getSafe(() -> podState.getLastTempBasalAmount()); + public final DateTime getTempBasalStartTime() { + return getSafe(() -> podState.getTempBasalStartTime()); } - public final Duration getLastTempBasalDuration() { - return getSafe(() -> podState.getLastTempBasalDuration()); + public final Double getTempBasalAmount() { + return getSafe(() -> podState.getTempBasalAmount()); } - public final void setLastTempBasal(DateTime startTime, Double amount, Duration duration) { - setAndStore(() -> { - podState.setLastTempBasalStartTime(startTime); - podState.setLastTempBasalAmount(amount); - podState.setLastTempBasalDuration(duration); - }); + public final Duration getTempBasalDuration() { + return getSafe(() -> podState.getTempBasalDuration()); + } + + public final boolean isTempBasalCertain() { + Boolean certain = getSafe(() -> podState.isTempBasalCertain()); + return certain == null || certain; + } + + public final void setTempBasal(DateTime startTime, Double amount, Duration duration, boolean certain) { + setTempBasal(startTime, amount, duration, certain, true); + } + + public final void setTempBasal(DateTime startTime, Double amount, Duration duration, Boolean certain, boolean store) { + DateTime currentStartTime = getTempBasalStartTime(); + Double currentAmount = getTempBasalAmount(); + Duration currentDuration = getTempBasalDuration(); + if (!Objects.equals(currentStartTime, startTime) || !Objects.equals(currentAmount, amount) || !Objects.equals(currentDuration, duration)) { + Runnable runnable = () -> { + podState.setTempBasalStartTime(startTime); + podState.setTempBasalAmount(amount); + podState.setTempBasalDuration(duration); + podState.setTempBasalCertain(certain); + }; + + if (store) { + setAndStore(runnable); + } else { + setSafe(runnable); + } + onTbrChanged(); + } + } + + /** + * @return true when a Temp Basal is stored in the Pod Stated + * Please note that this could also be an expired Temp Basal. For an indication on whether or not + * a temp basal is actually running, use {@link #isTempBasalRunning() isTempBasalRunning()} + */ + public final boolean hasTempBasal() { + return getTempBasalAmount() != null && getTempBasalDuration() != null && getTempBasalStartTime() != null; + } + + /** + * @return true when a Temp Basal is stored in the Pod Stated and this temp basal is currently running (based on start time and duration) + */ + public final boolean isTempBasalRunning() { + if (hasTempBasal()) { + DateTime tempBasalEndTime = getTempBasalStartTime().plus(getTempBasalDuration()); + return DateTime.now().isBefore(tempBasalEndTime); + } + return false; } public final DeliveryStatus getLastDeliveryStatus() { return getSafe(() -> podState.getLastDeliveryStatus()); } + public final Duration getExpirationAlertTimeBeforeShutdown() { + return getSafe(() -> podState.getExpirationAlertTimeBeforeShutdown()); + } + + public final void setExpirationAlertTimeBeforeShutdown(Duration duration) { + setAndStore(() -> podState.setExpirationAlertTimeBeforeShutdown(duration)); + } + + public final Integer getLowReservoirAlertUnits() { + return getSafe(() -> podState.getLowReservoirAlertUnits()); + } + + public final void setLowReservoirAlertUnits(Integer units) { + setAndStore(() -> podState.setLowReservoirAlertUnits(units)); + } + /** * Does not automatically store pod state in order to decrease I/O load */ @@ -404,15 +478,23 @@ public abstract class PodStateManager { podState.setReservoirLevel(statusResponse.getReservoirLevel()); podState.setTotalTicksDelivered(statusResponse.getTicksDelivered()); podState.setPodProgressStatus(statusResponse.getPodProgressStatus()); - if (!statusResponse.getDeliveryStatus().isTbrRunning()) { - podState.setLastTempBasalStartTime(null); - podState.setLastTempBasalAmount(null); - podState.setLastTempBasalDuration(null); + if (statusResponse.getDeliveryStatus().isTbrRunning()) { + if (!isTempBasalCertain() && isTempBasalRunning()) { + podState.setTempBasalCertain(true); + } + } else { + // Triggers {@link #onTbrChanged() onTbrChanged()} when appropriate + setTempBasal(null, null, null, true, false); } podState.setLastUpdatedFromResponse(DateTime.now()); }); } + protected void onTbrChanged() { + // Deliberately left empty + // Can be overridden in subclasses + } + private void setAndStore(Runnable runnable) { setSafe(runnable); storePodState(); @@ -507,9 +589,14 @@ public abstract class PodStateManager { private BasalSchedule basalSchedule; private DateTime lastBolusStartTime; private Double lastBolusAmount; - private Double lastTempBasalAmount; - private DateTime lastTempBasalStartTime; - private Duration lastTempBasalDuration; + private Duration lastBolusDuration; + private Boolean lastBolusCertain; + private Double tempBasalAmount; + private DateTime tempBasalStartTime; + private Duration tempBasalDuration; + private Boolean tempBasalCertain; + private Duration expirationAlertTimeBeforeShutdown; + private Integer lowReservoirAlertUnits; private final Map configuredAlerts = new HashMap<>(); private PodState(int address) { @@ -712,34 +799,74 @@ public abstract class PodStateManager { this.lastBolusAmount = lastBolusAmount; } - Double getLastTempBasalAmount() { - return lastTempBasalAmount; + Duration getLastBolusDuration() { + return lastBolusDuration; } - void setLastTempBasalAmount(Double lastTempBasalAmount) { - this.lastTempBasalAmount = lastTempBasalAmount; + void setLastBolusDuration(Duration lastBolusDuration) { + this.lastBolusDuration = lastBolusDuration; } - DateTime getLastTempBasalStartTime() { - return lastTempBasalStartTime; + Boolean isLastBolusCertain() { + return lastBolusCertain; } - void setLastTempBasalStartTime(DateTime lastTempBasalStartTime) { - this.lastTempBasalStartTime = lastTempBasalStartTime; + void setLastBolusCertain(Boolean certain) { + this.lastBolusCertain = certain; } - Duration getLastTempBasalDuration() { - return lastTempBasalDuration; + Double getTempBasalAmount() { + return tempBasalAmount; } - void setLastTempBasalDuration(Duration lastTempBasalDuration) { - this.lastTempBasalDuration = lastTempBasalDuration; + void setTempBasalAmount(Double tempBasalAmount) { + this.tempBasalAmount = tempBasalAmount; + } + + DateTime getTempBasalStartTime() { + return tempBasalStartTime; + } + + void setTempBasalStartTime(DateTime tempBasalStartTime) { + this.tempBasalStartTime = tempBasalStartTime; + } + + Duration getTempBasalDuration() { + return tempBasalDuration; + } + + void setTempBasalDuration(Duration tempBasalDuration) { + this.tempBasalDuration = tempBasalDuration; + } + + Boolean isTempBasalCertain() { + return tempBasalCertain; + } + + void setTempBasalCertain(Boolean certain) { + this.tempBasalCertain = certain; } Map getConfiguredAlerts() { return configuredAlerts; } + Duration getExpirationAlertTimeBeforeShutdown() { + return expirationAlertTimeBeforeShutdown; + } + + void setExpirationAlertTimeBeforeShutdown(Duration duration) { + expirationAlertTimeBeforeShutdown = duration; + } + + Integer getLowReservoirAlertUnits() { + return lowReservoirAlertUnits; + } + + void setLowReservoirAlertUnits(Integer units) { + lowReservoirAlertUnits = units; + } + @Override public String toString() { return "PodState{" + "address=" + address + @@ -766,9 +893,14 @@ public abstract class PodStateManager { ", basalSchedule=" + basalSchedule + ", lastBolusStartTime=" + lastBolusStartTime + ", lastBolusAmount=" + lastBolusAmount + - ", lastTempBasalAmount=" + lastTempBasalAmount + - ", lastTempBasalStartTime=" + lastTempBasalStartTime + - ", lastTempBasalDuration=" + lastTempBasalDuration + + ", lastBolusDuration=" + lastBolusDuration + + ", lastBolusCertain=" + lastBolusCertain + + ", tempBasalAmount=" + tempBasalAmount + + ", tempBasalStartTime=" + tempBasalStartTime + + ", tempBasalDuration=" + tempBasalDuration + + ", tempBasalCertain=" + tempBasalCertain + + ", expirationAlertHoursBeforeShutdown=" + expirationAlertTimeBeforeShutdown + + ", lowReservoirAlertUnits=" + lowReservoirAlertUnits + ", configuredAlerts=" + configuredAlerts + '}'; } diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/util/AlertConfigurationUtil.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/util/AlertConfigurationUtil.java index 4de5bcd290..0d223458d6 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/util/AlertConfigurationUtil.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/util/AlertConfigurationUtil.java @@ -11,19 +11,19 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.TimerAl import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.UnitsRemainingAlertTrigger; public class AlertConfigurationUtil { - public static AlertConfiguration createLowReservoirAlertConfiguration(Double units) { - return new AlertConfiguration(AlertType.LOW_RESERVOIR_ALERT, AlertSlot.SLOT4, true, false, Duration.ZERO, + public static AlertConfiguration createLowReservoirAlertConfiguration(boolean active, Double units) { + return new AlertConfiguration(AlertType.LOW_RESERVOIR_ALERT, AlertSlot.SLOT4, active, false, Duration.ZERO, new UnitsRemainingAlertTrigger(units), BeepType.BIP_BEEP_BIP_BEEP_BIP_BEEP_BIP_BEEP, BeepRepeat.EVERY_MINUTE_FOR_3_MINUTES_REPEAT_EVERY_60_MINUTES); } - public static AlertConfiguration createExpirationAdvisoryAlertConfiguration(Duration timeUntilAlert, Duration duration) { - return new AlertConfiguration(AlertType.EXPIRATION_ADVISORY_ALERT, AlertSlot.SLOT7, true, false, duration, - new TimerAlertTrigger(timeUntilAlert), BeepType.BIP_BEEP_BIP_BEEP_BIP_BEEP_BIP_BEEP, BeepRepeat.EVERY_MINUTE_FOR_3_MINUTES_REPEAT_EVERY_15_MINUTES); + public static AlertConfiguration createExpirationAdvisoryAlertConfiguration(boolean active, Duration timeUntilAlert, Duration duration) { + return new AlertConfiguration(AlertType.EXPIRATION_ADVISORY_ALERT, AlertSlot.SLOT7, active, false, duration, + new TimerAlertTrigger(timeUntilAlert), BeepType.BIP_BEEP_BIP_BEEP_BIP_BEEP_BIP_BEEP, BeepRepeat.EVERY_15_MINUTES); } public static AlertConfiguration createShutdownImminentAlertConfiguration(Duration timeUntilAlert) { return new AlertConfiguration(AlertType.SHUTDOWN_IMMINENT_ALARM, AlertSlot.SLOT2, true, false, Duration.ZERO, - new TimerAlertTrigger(timeUntilAlert), BeepType.BIP_BEEP_BIP_BEEP_BIP_BEEP_BIP_BEEP, BeepRepeat.EVERY_15_MINUTES); + new TimerAlertTrigger(timeUntilAlert), BeepType.BIP_BEEP_BIP_BEEP_BIP_BEEP_BIP_BEEP, BeepRepeat.EVERY_MINUTE_FOR_3_MINUTES_REPEAT_EVERY_15_MINUTES); } public static AlertConfiguration createAutoOffAlertConfiguration(boolean active, Duration countdownDuration) { diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/event/EventOmnipodTbrChanged.kt b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/event/EventOmnipodTbrChanged.kt new file mode 100644 index 0000000000..2b212506cd --- /dev/null +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/event/EventOmnipodTbrChanged.kt @@ -0,0 +1,8 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.event + +import info.nightscout.androidaps.events.Event + +/** + * Created by andy on 04.06.2018. + */ +class EventOmnipodTbrChanged : Event() \ No newline at end of file diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/manager/AapsOmnipodManager.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/manager/AapsOmnipodManager.java index 31681a4702..9ce1e2a2a5 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/manager/AapsOmnipodManager.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/manager/AapsOmnipodManager.java @@ -1,7 +1,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.manager; import android.content.Context; -import android.content.Intent; import org.joda.time.DateTime; import org.joda.time.Duration; @@ -14,7 +13,6 @@ import javax.inject.Inject; import javax.inject.Singleton; import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.activities.ErrorHelperActivity; import info.nightscout.androidaps.data.DetailedBolusInfo; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.PumpEnactResult; @@ -24,7 +22,6 @@ import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.events.Event; import info.nightscout.androidaps.interfaces.ActivePluginProvider; import info.nightscout.androidaps.interfaces.DatabaseHelperInterface; -import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; @@ -44,6 +41,8 @@ import info.nightscout.androidaps.plugins.pump.omnipod.definition.PodInitReceive import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoRecentPulseLog; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoResponse; +import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertConfiguration; +import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.DeliveryStatus; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.FaultEventCode; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType; @@ -75,6 +74,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.SetupActio import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodPumpValuesChanged; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; import info.nightscout.androidaps.plugins.pump.omnipod.util.AapsOmnipodUtil; +import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodAlertUtil; import info.nightscout.androidaps.utils.resources.ResourceHelper; import info.nightscout.androidaps.utils.sharedPreferences.SP; import io.reactivex.disposables.Disposable; @@ -94,6 +94,7 @@ public class AapsOmnipodManager { private final SP sp; private final OmnipodManager delegate; private final DatabaseHelperInterface databaseHelper; + private final OmnipodAlertUtil omnipodAlertUtil; private boolean basalBeepsEnabled; private boolean bolusBeepsEnabled; @@ -114,7 +115,8 @@ public class AapsOmnipodManager { HasAndroidInjector injector, ActivePluginProvider activePlugin, Context context, - DatabaseHelperInterface databaseHelper) { + DatabaseHelperInterface databaseHelper, + OmnipodAlertUtil omnipodAlertUtil) { if (podStateManager == null) { throw new IllegalArgumentException("Pod state manager can not be null"); } @@ -128,6 +130,7 @@ public class AapsOmnipodManager { this.context = context; this.databaseHelper = databaseHelper; this.sp = sp; + this.omnipodAlertUtil = omnipodAlertUtil; delegate = new OmnipodManager(aapsLogger, sp, communicationService, podStateManager); } @@ -153,17 +156,15 @@ public class AapsOmnipodManager { return new PumpEnactResult(injector).success(false).enacted(false).comment(getStringResource(R.string.omnipod_error_illegal_init_action_type, podInitActionType.name())); } - long time = System.currentTimeMillis(); - try { Disposable disposable = delegate.pairAndPrime().subscribe(res -> // - handleSetupActionResult(podInitActionType, podInitReceiver, res, time, null)); + handleSetupActionResult(podInitActionType, podInitReceiver, res, System.currentTimeMillis(), null)); return new PumpEnactResult(injector).success(true).enacted(true); } catch (Exception ex) { String comment = handleAndTranslateException(ex); podInitReceiver.returnInitTaskStatus(podInitActionType, false, comment); - addFailureToHistory(time, PodHistoryEntryType.PAIR_AND_PRIME, comment); + addFailureToHistory(System.currentTimeMillis(), PodHistoryEntryType.PAIR_AND_PRIME, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } } @@ -173,8 +174,6 @@ public class AapsOmnipodManager { return new PumpEnactResult(injector).success(false).enacted(false).comment(getStringResource(R.string.omnipod_error_illegal_init_action_type, podInitActionType.name())); } - long time = System.currentTimeMillis(); - try { BasalSchedule basalSchedule; try { @@ -182,8 +181,9 @@ public class AapsOmnipodManager { } catch (Exception ex) { throw new CommandInitializationException("Basal profile mapping failed", ex); } - Disposable disposable = delegate.insertCannula(basalSchedule).subscribe(res -> // - handleSetupActionResult(podInitActionType, podInitReceiver, res, time, profile)); + + Disposable disposable = delegate.insertCannula(basalSchedule, omnipodAlertUtil.getExpirationReminderTimeBeforeShutdown(), omnipodAlertUtil.getLowReservoirAlertUnits()).subscribe(res -> // + handleSetupActionResult(podInitActionType, podInitReceiver, res, System.currentTimeMillis(), profile)); rxBus.send(new EventDismissNotification(Notification.OMNIPOD_POD_NOT_ATTACHED)); @@ -193,36 +193,46 @@ public class AapsOmnipodManager { } catch (Exception ex) { String comment = handleAndTranslateException(ex); podInitReceiver.returnInitTaskStatus(podInitActionType, false, comment); - addFailureToHistory(time, PodHistoryEntryType.FILL_CANNULA_SET_BASAL_PROFILE, comment); + addFailureToHistory(PodHistoryEntryType.FILL_CANNULA_SET_BASAL_PROFILE, comment); + return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); + } + } + + public PumpEnactResult configureAlerts(List alertConfigurations) { + try { + StatusResponse statusResponse = delegate.configureAlerts(alertConfigurations); + addSuccessToHistory(PodHistoryEntryType.CONFIGURE_ALERTS, alertConfigurations); + return new PumpEnactResult(injector).success(true).enacted(false); + } catch (Exception ex) { + String comment = handleAndTranslateException(ex); + addFailureToHistory(PodHistoryEntryType.CONFIGURE_ALERTS, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } } public PumpEnactResult getPodStatus() { - long time = System.currentTimeMillis(); try { StatusResponse statusResponse = delegate.getPodStatus(); - addSuccessToHistory(time, PodHistoryEntryType.GET_POD_STATUS, statusResponse); + addSuccessToHistory(PodHistoryEntryType.GET_POD_STATUS, statusResponse); return new PumpEnactResult(injector).success(true).enacted(false); } catch (Exception ex) { String comment = handleAndTranslateException(ex); - addFailureToHistory(time, PodHistoryEntryType.GET_POD_STATUS, comment); + addFailureToHistory(PodHistoryEntryType.GET_POD_STATUS, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } } public PumpEnactResult deactivatePod(PodInitReceiver podInitReceiver) { - long time = System.currentTimeMillis(); try { delegate.deactivatePod(); } catch (Exception ex) { String comment = handleAndTranslateException(ex); podInitReceiver.returnInitTaskStatus(PodInitActionType.DEACTIVATE_POD_WIZARD_STEP, false, comment); - addFailureToHistory(time, PodHistoryEntryType.DEACTIVATE_POD, comment); + addFailureToHistory(PodHistoryEntryType.DEACTIVATE_POD, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } - addSuccessToHistory(time, PodHistoryEntryType.DEACTIVATE_POD, null); + addSuccessToHistory(PodHistoryEntryType.DEACTIVATE_POD, null); createSuspendedFakeTbrIfNotExists(); @@ -232,7 +242,6 @@ public class AapsOmnipodManager { } public PumpEnactResult setBasalProfile(Profile profile) { - long time = System.currentTimeMillis(); PodHistoryEntryType historyEntryType = podStateManager.isSuspended() ? PodHistoryEntryType.RESUME_DELIVERY : PodHistoryEntryType.SET_BASAL_SCHEDULE; try { @@ -244,31 +253,40 @@ public class AapsOmnipodManager { } delegate.setBasalSchedule(basalSchedule, isBasalBeepsEnabled()); - time = System.currentTimeMillis(); - // Because setting a basal profile actually suspends and then resumes delivery, TBR is implicitly cancelled if (historyEntryType == PodHistoryEntryType.RESUME_DELIVERY) { cancelSuspendedFakeTbrIfExists(); - } else { - reportImplicitlyCancelledTbr(time - 1000); } - addSuccessToHistory(time, historyEntryType, profile.getBasalValues()); + addSuccessToHistory(historyEntryType, profile.getBasalValues()); } catch (CommandFailedAfterChangingDeliveryStatusException ex) { createSuspendedFakeTbrIfNotExists(); String comment = getStringResource(R.string.omnipod_error_set_basal_failed_delivery_suspended); - showErrorDialog(comment, R.raw.boluserror); - addFailureToHistory(time, historyEntryType, comment); + showNotification(Notification.FAILED_UDPATE_PROFILE, comment, Notification.URGENT, R.raw.boluserror); + addFailureToHistory(historyEntryType, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } catch (DeliveryStatusVerificationFailedException ex) { - String comment = getStringResource(R.string.omnipod_error_set_basal_failed_delivery_might_be_suspended); - showErrorDialog(comment, R.raw.boluserror); - addFailureToHistory(time, historyEntryType, comment); + String comment; + if (ex.getExpectedStatus() == DeliveryStatus.SUSPENDED) { + // Happened when suspending delivery before setting the new profile + comment = getStringResource(R.string.omnipod_error_set_basal_failed_delivery_might_be_suspended); + } else { + // Happened when setting the new profile (after suspending delivery) + comment = getStringResource(R.string.omnipod_error_set_basal_might_have_failed_delivery_might_be_suspended); + } + showNotification(Notification.FAILED_UDPATE_PROFILE, comment, Notification.URGENT, R.raw.boluserror); + addFailureToHistory(historyEntryType, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } catch (Exception ex) { String comment = handleAndTranslateException(ex); - addFailureToHistory(time, historyEntryType, comment); + showNotification(Notification.FAILED_UDPATE_PROFILE, comment, Notification.URGENT, R.raw.boluserror); + addFailureToHistory(historyEntryType, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } + rxBus.send(new EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED)); + showNotification(Notification.PROFILE_SET_OK, + resourceHelper.gs(R.string.profile_set_ok), + Notification.INFO, null); + return new PumpEnactResult(injector).success(true).enacted(true); } @@ -306,8 +324,11 @@ public class AapsOmnipodManager { if (OmnipodManager.CommandDeliveryStatus.UNCERTAIN_FAILURE.equals(bolusCommandResult.getCommandDeliveryStatus())) { // For safety reasons, we treat this as a bolus that has successfully been delivered, in order to prevent insulin overdose - - showErrorDialog(getStringResource(R.string.omnipod_bolus_failed_uncertain), R.raw.boluserror); + if (detailedBolusInfo.isSMB) { + showNotification(getStringResource(R.string.omnipod_bolus_failed_uncertain_smb, detailedBolusInfo.insulin), Notification.URGENT, R.raw.boluserror); + } else { + showNotification(getStringResource(R.string.omnipod_bolus_failed_uncertain), Notification.URGENT, R.raw.boluserror); + } } detailedBolusInfo.date = bolusStarted.getTime(); @@ -382,7 +403,7 @@ public class AapsOmnipodManager { return new PumpEnactResult(injector).success(true).enacted(true); } catch (PodFaultException ex) { aapsLogger.debug(LTag.PUMP, "Successfully cancelled bolus (implicitly because of a Pod Fault)"); - showPodFaultErrorDialog(ex.getFaultEvent().getFaultEventCode()); + showPodFaultNotification(ex.getFaultEvent().getFaultEventCode()); addSuccessToHistory(System.currentTimeMillis(), PodHistoryEntryType.CANCEL_BOLUS, null); return new PumpEnactResult(injector).success(true).enacted(true); } catch (Exception ex) { @@ -397,48 +418,56 @@ public class AapsOmnipodManager { public PumpEnactResult setTemporaryBasal(TempBasalPair tempBasalPair) { boolean beepsEnabled = isTbrBeepsEnabled(); - long time = System.currentTimeMillis(); try { delegate.setTemporaryBasal(PumpType.Insulet_Omnipod.determineCorrectBasalSize(tempBasalPair.getInsulinRate()), Duration.standardMinutes(tempBasalPair.getDurationMinutes()), beepsEnabled, beepsEnabled); - time = System.currentTimeMillis(); } catch (CommandFailedAfterChangingDeliveryStatusException ex) { - reportImplicitlyCancelledTbr(time); String comment = getStringResource(R.string.omnipod_cancelled_old_tbr_failed_to_set_new); - addFailureToHistory(time, PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); + addFailureToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); + showNotification(comment, Notification.URGENT, null); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } catch (DeliveryStatusVerificationFailedException ex) { - String comment = getStringResource(R.string.omnipod_error_set_temp_basal_failed_old_tbr_might_be_cancelled); + String comment; + if (ex.getExpectedStatus() == DeliveryStatus.TEMP_BASAL_RUNNING) { + // Happened after cancelling the old TBR, when attempting to set new TBR + + comment = getStringResource(R.string.omnipod_error_set_temp_basal_failed_old_tbr_cancelled_new_might_have_failed); + long pumpId = addFailureToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); + + // Assume that setting the temp basal succeeded here, because in case it didn't succeed, + // The next StatusResponse that we receive will allow us to recover from the wrong state + // as we can see that the delivery status doesn't actually show that a TBR is running + // If we would assume that the TBR didn't succeed, we couldn't properly recover upon the next StatusResponse, + // as we could only see that the Pod is running a TBR, but we don't know the rate and duration as + // the Pod doesn't provide this information + addTempBasalTreatment(System.currentTimeMillis(), pumpId, tempBasalPair); + } else { + // Happened when attempting to cancel the old TBR + comment = getStringResource(R.string.omnipod_error_set_temp_basal_failed_old_tbr_might_be_cancelled); + addFailureToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); + } + showNotification(comment, Notification.URGENT, R.raw.boluserror); - addFailureToHistory(time, PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } catch (Exception ex) { String comment = handleAndTranslateException(ex); - addFailureToHistory(time, PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); + addFailureToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } - long pumpId = addSuccessToHistory(time, PodHistoryEntryType.SET_TEMPORARY_BASAL, tempBasalPair); + long pumpId = addSuccessToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, tempBasalPair); - TemporaryBasal tempStart = new TemporaryBasal(injector) // - .date(time) // - .duration(tempBasalPair.getDurationMinutes()) // - .absolute(tempBasalPair.getInsulinRate()) // - .pumpId(pumpId) - .source(Source.PUMP); - - activePlugin.getActiveTreatments().addToHistoryTempBasal(tempStart); + addTempBasalTreatment(System.currentTimeMillis(), pumpId, tempBasalPair); return new PumpEnactResult(injector).success(true).enacted(true); } public PumpEnactResult cancelTemporaryBasal() { - long time = System.currentTimeMillis(); try { delegate.cancelTemporaryBasal(isTbrBeepsEnabled()); - addSuccessToHistory(time, PodHistoryEntryType.CANCEL_TEMPORARY_BASAL, null); + addSuccessToHistory(PodHistoryEntryType.CANCEL_TEMPORARY_BASAL, null); } catch (Exception ex) { String comment = handleAndTranslateException(ex); - addFailureToHistory(time, PodHistoryEntryType.CANCEL_TEMPORARY_BASAL, comment); + addFailureToHistory(PodHistoryEntryType.CANCEL_TEMPORARY_BASAL, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } @@ -446,30 +475,27 @@ public class AapsOmnipodManager { } public PumpEnactResult acknowledgeAlerts() { - long time = System.currentTimeMillis(); try { delegate.acknowledgeAlerts(); - addSuccessToHistory(time, PodHistoryEntryType.ACKNOWLEDGE_ALERTS, null); + addSuccessToHistory(PodHistoryEntryType.ACKNOWLEDGE_ALERTS, null); } catch (Exception ex) { String comment = handleAndTranslateException(ex); - addFailureToHistory(time, PodHistoryEntryType.ACKNOWLEDGE_ALERTS, comment); + addFailureToHistory(PodHistoryEntryType.ACKNOWLEDGE_ALERTS, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } return new PumpEnactResult(injector).success(true).enacted(true); } public PumpEnactResult suspendDelivery() { - long time = System.currentTimeMillis(); - try { delegate.suspendDelivery(isBasalBeepsEnabled()); } catch (Exception ex) { String comment = handleAndTranslateException(ex); - addFailureToHistory(time, PodHistoryEntryType.SUSPEND_DELIVERY, comment); + addFailureToHistory(PodHistoryEntryType.SUSPEND_DELIVERY, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } - addSuccessToHistory(time, PodHistoryEntryType.SUSPEND_DELIVERY, null); + addSuccessToHistory(PodHistoryEntryType.SUSPEND_DELIVERY, null); createSuspendedFakeTbrIfNotExists(); @@ -478,27 +504,23 @@ public class AapsOmnipodManager { // Updates the pods current time based on the device timezone and the pod's time zone public PumpEnactResult setTime() { - long time = System.currentTimeMillis(); try { delegate.setTime(isBasalBeepsEnabled()); - time = System.currentTimeMillis(); - // Because set time actually suspends and then resumes delivery, TBR is implicitly cancelled - reportImplicitlyCancelledTbr(time - 1000); - addSuccessToHistory(time, PodHistoryEntryType.SET_TIME, null); + addSuccessToHistory(PodHistoryEntryType.SET_TIME, null); } catch (CommandFailedAfterChangingDeliveryStatusException ex) { createSuspendedFakeTbrIfNotExists(); String comment = getStringResource(R.string.omnipod_error_set_time_failed_delivery_suspended); - showErrorDialog(comment, R.raw.boluserror); - addFailureToHistory(time, PodHistoryEntryType.SET_TIME, comment); + showNotification(comment, Notification.URGENT, R.raw.boluserror); + addFailureToHistory(PodHistoryEntryType.SET_TIME, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } catch (DeliveryStatusVerificationFailedException ex) { String comment = getStringResource(R.string.omnipod_error_set_time_failed_delivery_might_be_suspended); - showErrorDialog(comment, R.raw.boluserror); - addFailureToHistory(time, PodHistoryEntryType.SET_TIME, comment); + showNotification(comment, Notification.URGENT, R.raw.boluserror); + addFailureToHistory(PodHistoryEntryType.SET_TIME, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } catch (Exception ex) { String comment = handleAndTranslateException(ex); - addFailureToHistory(time, PodHistoryEntryType.SET_TIME, comment); + addFailureToHistory(PodHistoryEntryType.SET_TIME, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } @@ -507,7 +529,7 @@ public class AapsOmnipodManager { public PodInfoRecentPulseLog readPulseLog() { PodInfoResponse response = delegate.getPodInfo(PodInfoType.RECENT_PULSE_LOG); - return response.getPodInfo(); + return (PodInfoRecentPulseLog) response.getPodInfo(); } public OmnipodRileyLinkCommunicationManager getCommunicationService() { @@ -593,27 +615,43 @@ public class AapsOmnipodManager { return false; } - private void reportImplicitlyCancelledTbr(long time) { - TreatmentsInterface plugin = activePlugin.getActiveTreatments(); - if (plugin.isTempBasalInProgress()) { - aapsLogger.debug(LTag.PUMP, "Reporting implicitly cancelled TBR to Treatments plugin"); + public void reportCancelledTbr() { + aapsLogger.debug(LTag.PUMP, "Reporting cancelled TBR to AAPS"); - long pumpId = addSuccessToHistory(time, PodHistoryEntryType.CANCEL_TEMPORARY_BASAL_BY_DRIVER, null); + long pumpId = addSuccessToHistory(PodHistoryEntryType.CANCEL_TEMPORARY_BASAL_BY_DRIVER, null); - TemporaryBasal temporaryBasal = new TemporaryBasal(injector) // - .date(time) // - .duration(0) // - .source(Source.PUMP) // - .pumpId(pumpId); + TemporaryBasal temporaryBasal = new TemporaryBasal(injector) // + .date(System.currentTimeMillis()) // + .duration(0) // + .source(Source.PUMP) // + .pumpId(pumpId); - plugin.addToHistoryTempBasal(temporaryBasal); - } + activePlugin.getActiveTreatments().addToHistoryTempBasal(temporaryBasal); + } + + private void addTempBasalTreatment(long time, long pumpId, TempBasalPair tempBasalPair) { + TemporaryBasal tempStart = new TemporaryBasal(injector) // + .date(time) // + .duration(tempBasalPair.getDurationMinutes()) // + .absolute(tempBasalPair.getInsulinRate()) // + .pumpId(pumpId) + .source(Source.PUMP); + + activePlugin.getActiveTreatments().addToHistoryTempBasal(tempStart); + } + + private long addSuccessToHistory(PodHistoryEntryType entryType, Object data) { + return addSuccessToHistory(System.currentTimeMillis(), entryType, data); } private long addSuccessToHistory(long requestTime, PodHistoryEntryType entryType, Object data) { return addToHistory(requestTime, entryType, data, true); } + private long addFailureToHistory(PodHistoryEntryType entryType, Object data) { + return addFailureToHistory(System.currentTimeMillis(), entryType, data); + } + private long addFailureToHistory(long requestTime, PodHistoryEntryType entryType, Object data) { return addToHistory(requestTime, entryType, data, false); } @@ -697,7 +735,7 @@ public class AapsOmnipodManager { comment = getStringResource(R.string.omnipod_driver_error_not_enough_data); } else if (ex instanceof PodFaultException) { FaultEventCode faultEventCode = ((PodFaultException) ex).getFaultEvent().getFaultEventCode(); - showPodFaultErrorDialog(faultEventCode); + showPodFaultNotification(faultEventCode); comment = createPodFaultErrorMessage(faultEventCode); } else if (ex instanceof PodReturnedErrorResponseException) { comment = getStringResource(R.string.omnipod_driver_error_pod_returned_error_response); @@ -725,26 +763,21 @@ public class AapsOmnipodManager { rxBus.send(event); } - private void showPodFaultErrorDialog(FaultEventCode faultEventCode) { - showPodFaultErrorDialog(faultEventCode, R.raw.boluserror); + private void showPodFaultNotification(FaultEventCode faultEventCode) { + showPodFaultNotification(faultEventCode, R.raw.boluserror); } - private void showPodFaultErrorDialog(FaultEventCode faultEventCode, Integer sound) { - showErrorDialog(createPodFaultErrorMessage(faultEventCode), sound); - } - - private void showErrorDialog(String message, Integer sound) { - Intent intent = new Intent(context, ErrorHelperActivity.class); - intent.putExtra("soundid", sound); - intent.putExtra("status", message); - intent.putExtra("title", resourceHelper.gs(R.string.error)); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); + private void showPodFaultNotification(FaultEventCode faultEventCode, Integer sound) { + showNotification(createPodFaultErrorMessage(faultEventCode), Notification.URGENT, sound); } private void showNotification(String message, int urgency, Integer sound) { + showNotification(Notification.OMNIPOD_PUMP_ALARM, message, urgency, sound); + } + + private void showNotification(int id, String message, int urgency, Integer sound) { Notification notification = new Notification( // - Notification.OMNIPOD_PUMP_ALARM, // + id, // message, // urgency); if (sound != null) { diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/manager/AapsPodStateManager.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/manager/AapsPodStateManager.java index a51da49f01..158c4472ad 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/manager/AapsPodStateManager.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/manager/AapsPodStateManager.java @@ -4,18 +4,22 @@ import javax.inject.Inject; import javax.inject.Singleton; import info.nightscout.androidaps.logging.AAPSLogger; +import info.nightscout.androidaps.plugins.bus.RxBusWrapper; import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodStorageKeys; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; +import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodTbrChanged; import info.nightscout.androidaps.utils.sharedPreferences.SP; @Singleton public class AapsPodStateManager extends PodStateManager { private final SP sp; + private final RxBusWrapper rxBus; @Inject - public AapsPodStateManager(AAPSLogger aapsLogger, SP sp) { + public AapsPodStateManager(AAPSLogger aapsLogger, SP sp, RxBusWrapper rxBus) { super(aapsLogger); this.sp = sp; + this.rxBus = rxBus; } @Override @@ -27,4 +31,8 @@ public class AapsPodStateManager extends PodStateManager { protected void storePodState(String podState) { sp.putString(OmnipodStorageKeys.Preferences.POD_STATE, podState); } + + @Override protected void onTbrChanged() { + rxBus.send(new EventOmnipodTbrChanged()); + } } diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/rileylink/manager/OmnipodRileyLinkCommunicationManager.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/rileylink/manager/OmnipodRileyLinkCommunicationManager.java index 1cde2f383b..19b6b6a4a0 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/rileylink/manager/OmnipodRileyLinkCommunicationManager.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/rileylink/manager/OmnipodRileyLinkCommunicationManager.java @@ -23,6 +23,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.mess import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.DeactivatePodCommand; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.ErrorResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusUpdatableResponse; +import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfo; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoFaultEvent; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.MessageBlockType; @@ -127,6 +128,11 @@ public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunication if (responseMessageBlock instanceof StatusUpdatableResponse) { podStateManager.updateFromResponse((StatusUpdatableResponse) responseMessageBlock); + } else if (responseMessageBlock instanceof PodInfoResponse) { + PodInfo podInfo = ((PodInfoResponse) responseMessageBlock).getPodInfo(); + if (podInfo instanceof StatusUpdatableResponse) { + podStateManager.updateFromResponse((StatusUpdatableResponse) podInfo); + } } if (responseClass.isInstance(responseMessageBlock)) { @@ -150,9 +156,10 @@ public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunication throw new PodReturnedErrorResponseException(error); } } else if (responseMessageBlock.getType() == MessageBlockType.POD_INFO_RESPONSE && ((PodInfoResponse) responseMessageBlock).getSubType() == PodInfoType.FAULT_EVENT) { - PodInfoFaultEvent faultEvent = ((PodInfoResponse) responseMessageBlock).getPodInfo(); + PodInfoFaultEvent faultEvent = (PodInfoFaultEvent) ((PodInfoResponse) responseMessageBlock).getPodInfo(); podStateManager.setFaultEvent(faultEvent); - podStateManager.setLastFailedCommunication(DateTime.now()); + // Treat as successful communication as the user will get notified and can work with this response + podStateManager.setLastSuccessfulCommunication(DateTime.now()); throw new PodFaultException(faultEvent); } else { podStateManager.setLastFailedCommunication(DateTime.now()); diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/ui/OmnipodFragment.kt b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/ui/OmnipodFragment.kt index 10ba98af39..4aec08bb70 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/ui/OmnipodFragment.kt +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/ui/OmnipodFragment.kt @@ -205,6 +205,8 @@ class OmnipodFragment : DaggerFragment() { private fun updateOmnipodStatus() { updateLastConnection() + updateLastBolus() + updateTempBasal() updatePodStatus() val errors = ArrayList(); @@ -225,11 +227,10 @@ class OmnipodFragment : DaggerFragment() { omnipod_pod_tid.text = PLACEHOLDER omnipod_pod_firmware_version.text = PLACEHOLDER omnipod_pod_expiry.text = PLACEHOLDER + omnipod_pod_expiry.setTextColor(Color.WHITE) omnipod_base_basal_rate.text = PLACEHOLDER omnipod_total_delivered.text = PLACEHOLDER omnipod_reservoir.text = PLACEHOLDER - omnipod_temp_basal.text = PLACEHOLDER - omnipod_last_bolus.text = PLACEHOLDER omnipod_pod_active_alerts.text = PLACEHOLDER } else { omnipod_pod_address.text = podStateManager.address.toString() @@ -237,10 +238,16 @@ class OmnipodFragment : DaggerFragment() { omnipod_pod_tid.text = podStateManager.tid.toString() omnipod_pod_firmware_version.text = resourceHelper.gs(R.string.omnipod_pod_firmware_version_value, podStateManager.pmVersion.toString(), podStateManager.piVersion.toString()) val expiresAt = podStateManager.expiresAt - omnipod_pod_expiry.text = if (expiresAt == null) { - PLACEHOLDER + if (expiresAt == null) { + omnipod_pod_expiry.text = PLACEHOLDER + omnipod_pod_expiry.setTextColor(Color.WHITE) } else { - dateUtil.dateAndTimeString(expiresAt.toDate()) + omnipod_pod_expiry.text = dateUtil.dateAndTimeString(expiresAt.toDate()) + omnipod_pod_expiry.setTextColor(if (DateTime.now().isAfter(expiresAt)) { + Color.RED + } else { + Color.WHITE + }) } if (podStateManager.hasFaultEvent()) { @@ -248,13 +255,6 @@ class OmnipodFragment : DaggerFragment() { errors.add(resourceHelper.gs(R.string.omnipod_pod_status_pod_fault_description, faultEventCode.value, faultEventCode.name)) } - // last bolus - omnipod_last_bolus.text = if (podStateManager.lastBolusStartTime != null && podStateManager.lastBolusAmount != null) { - resourceHelper.gs(R.string.omnipod_last_bolus, omnipodPumpPlugin.model().determineCorrectBolusSize(podStateManager.lastBolusAmount), resourceHelper.gs(R.string.insulin_unit_shortname), readableDuration(podStateManager.lastBolusStartTime)) - } else { - PLACEHOLDER - } - val now = DateTime.now() // base basal rate @@ -264,22 +264,6 @@ class OmnipodFragment : DaggerFragment() { PLACEHOLDER } - // Temp basal - val lastTempBasalStartTime = podStateManager.lastTempBasalStartTime; - val lastTempBasalAmount = podStateManager.lastTempBasalAmount - val lastTempBasalDuration = podStateManager.lastTempBasalDuration; - if (lastTempBasalStartTime != null && lastTempBasalAmount != null && lastTempBasalDuration != null) { - val endTime = lastTempBasalStartTime.plus(lastTempBasalDuration); - val minutesRunning = Duration(lastTempBasalStartTime, now).standardMinutes - omnipod_temp_basal.text = if (endTime.isAfter(now)) { - resourceHelper.gs(R.string.omnipod_temp_basal, lastTempBasalAmount, dateUtil.timeString(lastTempBasalStartTime.millis), minutesRunning, lastTempBasalDuration.standardMinutes) - } else { - PLACEHOLDER - } - } else { - omnipod_temp_basal.text = PLACEHOLDER - } - // total delivered omnipod_total_delivered.text = if (podStateManager.isPodActivationCompleted && podStateManager.totalInsulinDelivered != null) { resourceHelper.gs(R.string.omnipod_total_delivered, podStateManager.totalInsulinDelivered - OmnipodConstants.POD_SETUP_UNITS); @@ -371,6 +355,55 @@ class OmnipodFragment : DaggerFragment() { omnipod_pod_status.setTextColor(podStatusColor) } + private fun updateLastBolus() { + if (podStateManager.isPodActivationCompleted && podStateManager.hasLastBolus()) { + var text = resourceHelper.gs(R.string.omnipod_last_bolus, omnipodPumpPlugin.model().determineCorrectBolusSize(podStateManager.lastBolusAmount), resourceHelper.gs(R.string.insulin_unit_shortname), readableDuration(podStateManager.lastBolusStartTime)) + val textColor: Int + + if (podStateManager.isLastBolusCertain) { + textColor = Color.WHITE + } else { + textColor = Color.RED + text += " (" + resourceHelper.gs(R.string.omnipod_uncertain) + ")" + } + + omnipod_last_bolus.text = text; + omnipod_last_bolus.setTextColor(textColor) + + } else { + omnipod_last_bolus.text = PLACEHOLDER + omnipod_last_bolus.setTextColor(Color.WHITE) + } + } + + private fun updateTempBasal() { + if (podStateManager.isPodActivationCompleted && podStateManager.isTempBasalRunning) { + val now = DateTime.now() + + val startTime = podStateManager.tempBasalStartTime; + val amount = podStateManager.tempBasalAmount + val duration = podStateManager.tempBasalDuration; + + val minutesRunning = Duration(startTime, now).standardMinutes + + var text: String + val textColor: Int + text = resourceHelper.gs(R.string.omnipod_temp_basal, amount, dateUtil.timeString(startTime.millis), minutesRunning, duration.standardMinutes) + if (podStateManager.isTempBasalCertain) { + textColor = Color.WHITE + } else { + textColor = Color.RED + text += " (" + resourceHelper.gs(R.string.omnipod_uncertain) + ")" + } + + omnipod_temp_basal.text = text; + omnipod_temp_basal.setTextColor(textColor) + } else { + omnipod_temp_basal.text = PLACEHOLDER + omnipod_temp_basal.setTextColor(Color.WHITE) + } + } + private fun updateQueueStatus() { if (isQueueEmpty()) { omnipod_queue.visibility = View.GONE diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/ui/wizard/pages/InitPodRefreshAction.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/ui/wizard/pages/InitPodRefreshAction.java index 6949710a67..9f7c47be2b 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/ui/wizard/pages/InitPodRefreshAction.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/ui/wizard/pages/InitPodRefreshAction.java @@ -12,6 +12,7 @@ import dagger.android.HasAndroidInjector; import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.interfaces.DatabaseHelperInterface; +import info.nightscout.androidaps.interfaces.ProfileFunction; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; @@ -27,14 +28,17 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP; public class InitPodRefreshAction extends AbstractCancelAction implements FinishActionInterface { private final PodActionType actionType; + private final HasAndroidInjector injector; @Inject PodStateManager podStateManager; @Inject AAPSLogger aapsLogger; @Inject SP sp; @Inject NSUpload nsUpload; @Inject DatabaseHelperInterface databaseHelper; + @Inject ProfileFunction profileFunction; public InitPodRefreshAction(HasAndroidInjector injector, PodActionType actionType) { + this.injector = injector; injector.androidInjector().inject(this); this.actionType = actionType; } @@ -50,6 +54,8 @@ public class InitPodRefreshAction extends AbstractCancelAction implements Finish public void execute() { if (actionType == PodActionType.INIT_POD) { if (podStateManager.isPodRunning()) { + uploadCareportalEvent(System.currentTimeMillis() - 2000, CareportalEvent.PUMPBATTERYCHANGE); + uploadCareportalEvent(System.currentTimeMillis() - 1000, CareportalEvent.INSULINCHANGE); uploadCareportalEvent(System.currentTimeMillis(), CareportalEvent.SITECHANGE); } } @@ -61,10 +67,14 @@ public class InitPodRefreshAction extends AbstractCancelAction implements Finish try { JSONObject data = new JSONObject(); String enteredBy = sp.getString("careportal_enteredby", ""); - if (!enteredBy.equals("")) data.put("enteredBy", enteredBy); + if (enteredBy.isEmpty()) { + data.put("enteredBy", enteredBy); + } data.put("created_at", DateUtil.toISOString(date)); + data.put("mills", date); data.put("eventType", event); - CareportalEvent careportalEvent = new CareportalEvent(); + data.put("units", profileFunction.getUnits()); + CareportalEvent careportalEvent = new CareportalEvent(injector); careportalEvent.date = date; careportalEvent.source = Source.USER; careportalEvent.eventType = event; diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/util/OmnipodAlertUtil.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/util/OmnipodAlertUtil.java new file mode 100644 index 0000000000..f9e541f986 --- /dev/null +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/util/OmnipodAlertUtil.java @@ -0,0 +1,29 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.util; + +import org.joda.time.Duration; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodStorageKeys; +import info.nightscout.androidaps.utils.sharedPreferences.SP; + +@Singleton +public class OmnipodAlertUtil { + private final SP sp; + + @Inject + public OmnipodAlertUtil(SP sp) { + this.sp = sp; + } + + public Duration getExpirationReminderTimeBeforeShutdown() { + boolean expirationAlertEnabled = sp.getBoolean(OmnipodStorageKeys.Preferences.EXPIRATION_REMINDER_ENABLED, true); + return expirationAlertEnabled ? Duration.standardHours(sp.getInt(OmnipodStorageKeys.Preferences.EXPIRATION_REMINDER_HOURS_BEFORE_SHUTDOWN, 9)) : null; + } + + public Integer getLowReservoirAlertUnits() { + boolean lowReservoirAlertEnabled = sp.getBoolean(OmnipodStorageKeys.Preferences.LOW_RESERVOIR_ALERT_ENABLED, true); + return lowReservoirAlertEnabled ? sp.getInt(OmnipodStorageKeys.Preferences.LOW_RESERVOIR_ALERT_UNITS, 20) : null; + } +} diff --git a/omnipod/src/main/res/drawable/ic_exit_to_app.xml b/omnipod/src/main/res/drawable/ic_exit_to_app.xml new file mode 100644 index 0000000000..37a6c7d058 --- /dev/null +++ b/omnipod/src/main/res/drawable/ic_exit_to_app.xml @@ -0,0 +1,12 @@ + + + + diff --git a/omnipod/src/main/res/layout/omnipod_initpod.xml b/omnipod/src/main/res/layout/omnipod_initpod.xml index 160f668963..af83f9583c 100644 --- a/omnipod/src/main/res/layout/omnipod_initpod.xml +++ b/omnipod/src/main/res/layout/omnipod_initpod.xml @@ -4,8 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" - tools:context=".plugins.pump.omnipod.dialogs.InitPodWizard"> + android:orientation="vertical"> @@ -75,7 +74,7 @@ android:layout_height="wrap_content" android:background="@android:color/transparent" android:onClick="showPreviousPage" - android:text="@string/previous_button" + android:text="@string/omnipod_wizard_button_previous" android:textColor="#FFFFFF" />