Merge branch 'omnipod_eros_dev' into omnipod_eros_dev_upstream_merge

This commit is contained in:
Bart Sopers 2020-09-03 23:54:34 +02:00
commit a708aad267
53 changed files with 1066 additions and 430 deletions

View file

@ -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

View file

@ -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'

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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()

View file

@ -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

View file

@ -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);

View file

@ -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";

View file

@ -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;
}
}

View file

@ -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
}
}

View file

@ -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()
}
}

View file

@ -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

View file

@ -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<AlertConfiguration> 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++;

View file

@ -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
}
}

View file

@ -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);

View file

@ -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 {

View file

@ -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),

View file

@ -28,9 +28,17 @@ public class ConfigureAlertsAction implements OmnipodAction<StatusResponse> {
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<AlertConfiguration> alertConfigurations) {
for (AlertConfiguration alertConfiguration : alertConfigurations) {
if (alertConfiguration.isActive()) {
podStateManager.putConfiguredAlert(alertConfiguration.getAlertSlot(), alertConfiguration.getAlertType());
} else {
podStateManager.removeConfiguredAlert(alertConfiguration.getAlertSlot());
}
}
}
}

View file

@ -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<StatusResponse> {
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<StatusResponse> {
}
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<StatusResponse> {
throw new IllegalPodProgressException(null, podStateManager.getPodProgressStatus());
}
}
private List<AlertConfiguration> 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();
}
}

View file

@ -46,8 +46,6 @@ public class SetBasalScheduleAction implements OmnipodAction<StatusResponse> {
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);
}
}

View file

@ -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<StatusResponse> {
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);
}
}

View file

@ -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<AlertSlot, AlertConfiguration> 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<AlertConfiguration> build() {
return new ArrayList<>(alerts.values());
}
}

View file

@ -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<AlertConfiguration> 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));
}
}

View file

@ -21,8 +21,8 @@ public class PodInfoResponse extends MessageBlock {
return subType;
}
public <T extends PodInfo> T getPodInfo() {
return (T) podInfo;
public PodInfo getPodInfo() {
return podInfo;
}
@Override

View file

@ -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 +
'}';
}
}

View file

@ -1,6 +1,6 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.definition;
abstract class AlertTrigger<T> {
public abstract class AlertTrigger<T> {
private final T value;
AlertTrigger(T value) {
@ -10,4 +10,10 @@ abstract class AlertTrigger<T> {
public T getValue() {
return value;
}
@Override public String toString() {
return "AlertTrigger{" +
"value=" + value +
'}';
}
}

View file

@ -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<SetupActionResult> insertCannula(BasalSchedule basalSchedule) {
public synchronized Single<SetupActionResult> 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<AlertConfiguration> 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<BolusDeliveryResult> 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<BolusDeliveryResult> bolusCompletionSubject;
private final CompositeDisposable disposables;
private ActiveBolusData(double units, DateTime startDate, SingleSubject<BolusDeliveryResult> bolusCompletionSubject, CompositeDisposable disposables) {
private ActiveBolusData(double units, DateTime startDate, CommandDeliveryStatus commandDeliveryStatus, SingleSubject<BolusDeliveryResult> 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;
}

View file

@ -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<AlertSlot, AlertType> 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<AlertSlot, AlertType> 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 +
'}';
}

View file

@ -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) {

View file

@ -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()

View file

@ -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<AlertConfiguration> 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) {

View file

@ -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());
}
}

View file

@ -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());

View file

@ -205,6 +205,8 @@ class OmnipodFragment : DaggerFragment() {
private fun updateOmnipodStatus() {
updateLastConnection()
updateLastBolus()
updateTempBasal()
updatePodStatus()
val errors = ArrayList<String>();
@ -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

View file

@ -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;

View file

@ -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;
}
}

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M16.495,12.092l-4.921,-4.92l-1.444,1.444l2.468,2.467l-9.765,0l0,2.042l9.765,0l-2.468,2.467l1.444,1.444l4.921,-4.92l-0.012,-0.012z"
android:fillColor="@color/white"/>
<path
android:pathData="M18.682,2.975H5.152c-1.272,0 -2.307,1.035 -2.307,2.307v3.801h2.126V5.282c0,-0.1 0.081,-0.181 0.181,-0.181h13.53c0.1,0 0.181,0.081 0.181,0.181v13.436c0,0.1 -0.081,0.181 -0.181,0.181H5.152c-0.1,0 -0.181,-0.081 -0.181,-0.181v-3.593H2.845v3.593c0,1.272 1.035,2.307 2.307,2.307h13.53c1.272,0 2.308,-1.034 2.308,-2.307V5.282C20.989,4.01 19.954,2.975 18.682,2.975z"
android:fillColor="@color/white"/>
</vector>

View file

@ -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">
<LinearLayout
android:layout_width="wrap_content"
@ -29,7 +28,7 @@
android:layout_gravity="bottom"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:text="@string/exit"
android:text="@string/omnipod_wizard_button_exit"
android:textAppearance="@android:style/TextAppearance.Material.Medium"
android:textStyle="bold" />
</LinearLayout>
@ -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" />
<View
@ -89,7 +88,7 @@
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:onClick="showNextPage"
android:text="@string/next_button"
android:text="@string/omnipod_wizard_button_next"
android:textColor="#FFFFFF" />
<Button
@ -98,7 +97,7 @@
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:onClick="finishSetupWizard"
android:text="@string/setupwizard_finish"
android:text="@string/omnipod_wizard_button_finish"
android:textColor="#FFFFFF" />
</LinearLayout>

View file

@ -42,8 +42,8 @@
android:id="@+id/initAction_textErrorMessage"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_marginLeft="15dp"
android:layout_marginRight="5dp"
android:layout_marginStart="15dp"
android:layout_marginEnd="5dp"
android:textSize="15sp"
android:layout_weight="0.3"
style="@style/WizardPagePodContent"
@ -54,8 +54,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.7"
android:layout_marginLeft="5dp"
android:layout_marginRight="15dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="15dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:drawableTop="@drawable/ic_actions_refill"

View file

@ -41,7 +41,7 @@
<ListView android:id="@+id/podInfoList"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:saveEnabled="false"

View file

@ -20,8 +20,8 @@
android:minHeight="@dimen/list_item_height_small"
android:orientation="horizontal"
android:baselineAligned="true"
android:paddingLeft="@dimen/list_item_padding_left"
android:paddingRight="@dimen/list_item_padding_right"
android:paddingStart="@dimen/list_item_padding_left"
android:paddingEnd="@dimen/list_item_padding_right"
android:paddingTop="12dp"
android:paddingBottom="12dp">
@ -33,7 +33,7 @@
android:ellipsize="end"
android:layout_width="100sp"
android:layout_height="wrap_content"
android:layout_marginRight="16dp" />
android:layout_marginEnd="16dp" />
<TextView android:id="@android:id/text2"
style="?android:textAppearanceMedium"

View file

@ -1,11 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".plugins.pump.omnipod.dialogs.PodHistoryActivity">
android:paddingBottom="@dimen/activity_vertical_margin">
<LinearLayout
android:id="@+id/omnipod_historytop"
@ -17,28 +15,27 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="20dp"
android:text="Type:"
android:layout_marginStart="5dp"
android:layout_marginEnd="20dp"
android:text="@string/omnipod_history_type"
android:textAppearance="?android:attr/textAppearanceSmall" />
<Spinner
android:id="@+id/omnipod_historytype"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="20dp" />
android:layout_marginEnd="20dp" />
<TextView
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginRight="5dp"
android:layout_marginEnd="5dp"
android:layout_weight="1"
android:background="@drawable/pillborder"
android:gravity="center_horizontal"
android:text="@string/omnipod_cmd_pod_history" />
</LinearLayout>
<TextView

View file

@ -1,33 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="40dp"
android:paddingLeft="20dp">
android:orientation="horizontal"
android:paddingStart="20dp">
<TextView
android:id="@+id/omnipod_history_time"
android:layout_width="88dp"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="Date"
android:textSize="12dp" />
android:text="@string/omnipod_history_item_date"
android:textSize="12sp" />
<TextView
android:id="@+id/omnipod_history_source"
android:layout_width="132dp"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="Source"
android:textSize="12dp" />
android:text="@string/omnipod_history_item_source"
android:textSize="12sp" />
<TextView
android:id="@+id/omnipod_history_description"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:text="Description"
android:gravity="center_vertical"
android:textSize="12dp" />
android:text="@string/omnipod_history_item_description"
android:textSize="12sp" />
</LinearLayout>

View file

@ -46,7 +46,7 @@
<TextView
android:id="@+id/initpod_waiting_for_rl_text"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_height="0dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="3dp"
android:layout_marginRight="10dp"

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black_overlay">#66000000</color>
<color name="ribbonWarning">#f4d700</color>
</resources>

View file

@ -5,7 +5,7 @@
<!-- Omnipod - Base -->
<string name="omnipod_name" translatable="false">Omnipod</string>
<string name="omnipod_name_short" translatable="false">POD</string>
<string name="omnipod_name_short" translatable="false">Pod</string>
<string name="description_pump_omnipod">Pump integration for Omnipod, requires RileyLink (with at least 2.0 firmware) device.</string>
<!-- Omnipod Configuration -->
@ -16,6 +16,10 @@
<string name="key_omnipod_suspend_delivery_button_enabled" translatable="false">AAPS.Omnipod.suspend_delivery_button_enabled</string>
<string name="key_omnipod_pulse_log_button_enabled" translatable="false">AAPS.Omnipod.pulse_log_button_enabled</string>
<string name="key_omnipod_time_change_event_enabled" translatable="false">AAPS.Omnipod.time_change_enabled</string>
<string name="key_omnipod_expiration_reminder_enabled" translatable="false">AAPS.Omnipod.expiration_reminder_enabled</string>
<string name="key_omnipod_expiration_reminder_hours_before_shutdown" translatable="false">AAPS.Omnipod.expiration_reminder_hours_before_shutdown</string>
<string name="key_omnipod_low_reservoir_alert_enabled" translatable="false">AAPS.Omnipod.low_reservoir_alert_enabled</string>
<string name="key_omnipod_low_reservoir_alert_units" translatable="false">AAPS.Omnipod.low_reservoir_alert_units</string>
<string name="omnipod_config_bolus_beeps_enabled">Bolus beeps enabled</string>
<string name="omnipod_config_basal_beeps_enabled">Basal beeps enabled</string>
<string name="omnipod_config_smb_beeps_enabled">SMB beeps enabled</string>
@ -23,6 +27,10 @@
<string name="omnipod_config_suspend_delivery_button_enabled">Suspend Delivery button enabled</string>
<string name="omnipod_config_pulse_log_button_enabled">Pulse Log button enabled</string>
<string name="omnipod_config_time_change_enabled">DST/Time zone detection enabled</string>
<string name="omnipod_config_expiration_reminder_enabled">Expiration reminder enabled</string>
<string name="omnipod_config_expiration_reminder_hours_before_shutdown">Hours before shutdown</string>
<string name="omnipod_config_low_reservoir_alert_enabled">Low reservoir alert enabled</string>
<string name="omnipod_config_low_reservoir_alert_units">Number of units</string>
<!-- Omnipod - Fragment -->
<string name="omnipod_moments_ago">Moments ago</string>
@ -133,12 +141,15 @@
<string name="omnipod_alert_shutdown_imminent">Shutdown is imminent</string>
<string name="omnipod_alert_low_reservoir">Low reservoir</string>
<string name="omnipod_alert_unknown_alert">Unknown alert</string>
<string name="omnipod_error_set_basal_failed_delivery_might_be_suspended">Setting basal profile might have failed. Delivery might be suspended! Please refresh Pod status and resume delivery from the Omnipod tab if needed.</string>
<string name="omnipod_error_set_basal_failed_delivery_suspended">Setting basal profile failed. Delivery is suspended! Please resume delivery from the Omnipod tab.</string>
<string name="omnipod_error_set_temp_basal_failed_old_tbr_might_be_cancelled">Setting temp basal might have failed. If a temp basal was already running, it might have been cancelled without AndroidAPS being aware!</string>
<string name="omnipod_error_set_time_failed_delivery_might_be_suspended">Setting time might have failed. Delivery might be suspended! Please refresh Pod status and resume delivery from the Omnipod tab if needed.</string>
<string name="omnipod_error_set_time_failed_delivery_suspended">Setting time failed. Delivery is suspended! Please resume delivery from the Omnipod tab.</string>
<string name="omnipod_bolus_failed_uncertain">Unable to verify whether the bolus succeeded. Please verify that your Pod is bolusing or cancel the bolus.</string>
<string name="omnipod_error_set_basal_failed_delivery_might_be_suspended">Setting basal profile failed. Delivery might be suspended! Please manually refresh the Pod status from the Omnipod tab and resume delivery if needed.</string>
<string name="omnipod_error_set_basal_might_have_failed_delivery_might_be_suspended">Setting basal profile might have failed. Delivery might be suspended! Please manually refresh the Pod status from the Omnipod tab and resume delivery if needed.</string>
<string name="omnipod_error_set_basal_failed_delivery_suspended">Setting basal profile failed. Delivery is suspended! Please manually resume delivery from the Omnipod tab.</string>
<string name="omnipod_error_set_temp_basal_failed_old_tbr_might_be_cancelled">Setting temp basal failed. If a temp basal was previously running, it might have been cancelled. Please manually refresh the Pod status from the Omnipod tab.</string>
<string name="omnipod_error_set_temp_basal_failed_old_tbr_cancelled_new_might_have_failed">Setting temp might have basal failed. If a temp basal was previously running, it has been cancelled. Please manually refresh the Pod status from the Omnipod tab.</string>
<string name="omnipod_error_set_time_failed_delivery_might_be_suspended">Setting time might have failed. Delivery might be suspended! Please manually refresh the Pod status from the Omnipod tab and resume delivery if needed.</string>
<string name="omnipod_error_set_time_failed_delivery_suspended">Setting time failed. Delivery is suspended! Please manually resume delivery from the Omnipod tab.</string>
<string name="omnipod_bolus_failed_uncertain">Unable to verify whether the bolus succeeded. Please manually verify that your Pod is bolusing by listening to clicks. <b>If you are sure that the bolus didn\'t succeed, you should manually delete the bolus entry from Treatments, even if you click \'Cancel bolus\' now!</b></string>
<string name="omnipod_bolus_failed_uncertain_smb">Unable to verify whether SMB bolus (%1$.2f U) succeeded. <b>If you are sure that the Bolus didn\'t succeed, you should manually delete the SMB entry from Treatments.</b></string>
<string name="omnipod_rl_stats">RL stats</string>
<string name="omnipod_read_pulse_log_short">Pulse log</string>
<string name="omnipod_pod_lot">LOT</string>
@ -165,6 +176,20 @@
<string name="omnipod_cancelled_old_tbr_failed_to_set_new">Cancelled the old temporary basal, but failed to set new temporary basal</string>
<string name="omnipod_cmd_set_fake_suspended_tbr">Set fake temporary basal because the Pod is suspended</string>
<string name="omnipod_cmd_cancel_fake_suspended_tbr">Cancel fake temporary basal that was created because the Pod was suspended</string>
<string name="omnipod_uncertain">uncertain</string>
<string name="omnipod_expiration_alerts_updated">Alert configuration updated in Pod</string>
<string name="omnipod_preference_category_rileylink">RileyLink</string>
<string name="omnipod_preference_category_other">Other</string>
<string name="omnipod_preference_category_alerts">Alerts</string>
<string name="omnipod_preference_category_confirmation_beeps">Confirmation beeps</string>
<string name="omnipod_wizard_button_exit">Exit</string>
<string name="omnipod_wizard_button_previous">Previous</string>
<string name="omnipod_wizard_button_next">Next</string>
<string name="omnipod_wizard_button_finish">Finish</string>
<string name="omnipod_history_item_description">Description</string>
<string name="omnipod_history_item_source">Source</string>
<string name="omnipod_history_item_date">Date</string>
<string name="omnipod_history_type">Type:</string>
<plurals name="omnipod_minutes">
<item quantity="one">%1$d minute</item>

View file

@ -4,8 +4,8 @@
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginBottom">8dp</item>
<item name="android:paddingLeft">20px</item>
<item name="android:paddingRight">20px</item>
<item name="android:paddingLeft">20dp</item>
<item name="android:paddingRight">20dp</item>
<item name="android:textSize">18sp</item>
</style>
@ -14,10 +14,10 @@
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginBottom">20dp</item>
<item name="android:layout_marginTop">20dp</item>
<item name="android:paddingTop">20px</item>
<item name="android:paddingBottom">20px</item>
<item name="android:paddingLeft">60px</item>
<item name="android:paddingRight">60px</item>
<item name="android:paddingTop">20dp</item>
<item name="android:paddingBottom">20dp</item>
<item name="android:paddingLeft">60dp</item>
<item name="android:paddingRight">60dp</item>
<item name="android:textSize">18sp</item>
</style>

View file

@ -1,10 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:validate="http://schemas.android.com/apk/res-auto">
<PreferenceCategory
android:title="@string/omnipod_name"
app:initialExpandedChildrenCount="0">
<PreferenceCategory android:title="@string/omnipod_preference_category_rileylink">
<Preference
android:enabled="true"
@ -14,6 +12,10 @@
<intent android:action="info.nightscout.androidaps.plugins.PumpCommon.dialog.RileyLinkBLEScanActivity" />
</Preference>
</PreferenceCategory>
<PreferenceCategory android:title="@string/omnipod_preference_category_confirmation_beeps">
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_omnipod_bolus_beeps_enabled"
@ -34,6 +36,46 @@
android:key="@string/key_omnipod_tbr_beeps_enabled"
android:title="@string/omnipod_config_tbr_beeps_enabled" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/omnipod_preference_category_alerts">
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_omnipod_expiration_reminder_enabled"
android:title="@string/omnipod_config_expiration_reminder_enabled" />
<info.nightscout.androidaps.utils.textValidator.ValidatingEditTextPreference
android:defaultValue="9"
android:dependency="@string/key_omnipod_expiration_reminder_enabled"
android:digits="0123456789"
android:inputType="number"
android:key="@string/key_omnipod_expiration_reminder_hours_before_shutdown"
android:title="@string/omnipod_config_expiration_reminder_hours_before_shutdown"
validate:maxNumber="24"
validate:minNumber="2"
validate:testType="numericRange" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_omnipod_low_reservoir_alert_enabled"
android:title="@string/omnipod_config_low_reservoir_alert_enabled" />
<info.nightscout.androidaps.utils.textValidator.ValidatingEditTextPreference
android:defaultValue="20"
android:dependency="@string/key_omnipod_low_reservoir_alert_enabled"
android:digits="0123456789"
android:inputType="number"
android:key="@string/key_omnipod_low_reservoir_alert_units"
android:title="@string/omnipod_config_low_reservoir_alert_units"
validate:maxNumber="50"
validate:minNumber="5"
validate:testType="numericRange" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/omnipod_preference_category_other">
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_omnipod_suspend_delivery_button_enabled"
@ -50,4 +92,5 @@
android:title="@string/omnipod_config_time_change_enabled" />
</PreferenceCategory>
</androidx.preference.PreferenceScreen>

View file

@ -41,7 +41,7 @@ public class PodInfoResponseTest {
assertEquals(PodInfoType.FAULT_EVENT, podInfoResponse.getSubType());
PodInfoFaultEvent podInfo = podInfoResponse.getPodInfo();
PodInfoFaultEvent podInfo = (PodInfoFaultEvent) podInfoResponse.getPodInfo();
assertFalse(podInfo.isFaultAccessingTables());
assertEquals(LogEventErrorCode.INTERNAL_2_BIT_VARIABLE_SET_AND_MANIPULATED_IN_MAIN_LOOP_ROUTINES_2, podInfo.getLogEventErrorType());
}
@ -53,6 +53,6 @@ public class PodInfoResponseTest {
assertEquals(PodInfoType.FAULT_EVENT, podInfoResponse.getSubType());
thrown.expect(ClassCastException.class);
PodInfoActiveAlerts podInfo = podInfoResponse.getPodInfo();
PodInfoActiveAlerts podInfo = (PodInfoActiveAlerts) podInfoResponse.getPodInfo();
}
}

View file

@ -11,6 +11,7 @@ import org.mockito.Mock;
import org.powermock.modules.junit4.PowerMockRunner;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.FirmwareVersion;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
@ -21,6 +22,7 @@ import static org.junit.Assert.assertEquals;
public class AapsPodStateManagerTest {
@Mock AAPSLogger aapsLogger;
@Mock SP sp;
@Mock RxBusWrapper rxBus;
@Test
public void times() {
@ -31,7 +33,7 @@ public class AapsPodStateManagerTest {
DateTimeUtils.setCurrentMillisFixed(now.getMillis());
AapsPodStateManager podStateManager = new AapsPodStateManager(aapsLogger, sp);
AapsPodStateManager podStateManager = new AapsPodStateManager(aapsLogger, sp, rxBus);
podStateManager.initState(0x0);
podStateManager.setInitializationParameters(0, 0, new FirmwareVersion(1, 1, 1),
new FirmwareVersion(2, 2, 2), timeZone, PodProgressStatus.ABOVE_FIFTY_UNITS);
@ -49,7 +51,7 @@ public class AapsPodStateManagerTest {
DateTimeUtils.setCurrentMillisFixed(now.getMillis());
AapsPodStateManager podStateManager = new AapsPodStateManager(aapsLogger, sp);
AapsPodStateManager podStateManager = new AapsPodStateManager(aapsLogger, sp, rxBus);
podStateManager.initState(0x0);
podStateManager.setInitializationParameters(0, 0, new FirmwareVersion(1, 1, 1),
new FirmwareVersion(2, 2, 2), timeZone, PodProgressStatus.ABOVE_FIFTY_UNITS);
@ -72,7 +74,7 @@ public class AapsPodStateManagerTest {
DateTimeUtils.setCurrentMillisFixed(now.getMillis());
AapsPodStateManager podStateManager = new AapsPodStateManager(aapsLogger, sp);
AapsPodStateManager podStateManager = new AapsPodStateManager(aapsLogger, sp, rxBus);
podStateManager.initState(0x0);
podStateManager.setInitializationParameters(0, 0, new FirmwareVersion(1, 1, 1),
new FirmwareVersion(2, 2, 2), timeZone, PodProgressStatus.ABOVE_FIFTY_UNITS);

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.dependencyInjection
package info.nightscout.androidaps.plugins.pump.common.dagger
import dagger.Module
import dagger.android.ContributesAndroidInjector