diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt index f64eb9430a..124f00e4ac 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt @@ -45,7 +45,6 @@ import info.nightscout.shared.SafeParse import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign import java.text.DateFormat -import java.text.DecimalFormat import java.text.SimpleDateFormat import java.util.* import java.util.concurrent.TimeUnit @@ -111,9 +110,11 @@ class ActionStringHandler @Inject constructor( @Synchronized private fun handleInitiate(actionString: String) { + //TODO: i18n + Log.i("ActionStringHandler", "handleInitiate actionString=" + actionString) if (!sp.getBoolean(R.string.key_wear_control, false)) return lastBolusWizard = null - var rTitle = "CONFIRM" //TODO: i18n + var rTitle = rh.gs(R.string.confirm).uppercase() var rMessage = "" var rAction = "" // do the parsing and check constraints @@ -140,6 +141,11 @@ class ActionStringHandler @Inject constructor( val carbs = SafeParse.stringToInt(act[2]) val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value() val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value() + val pump = activePlugin.activePump + if (insulinAfterConstraints > 0 && (!pump.isInitialized() || pump.isSuspended() || loop.isDisconnected)) { + sendError(rh.gs(R.string.wizard_pump_not_available)) + return + } rMessage += rh.gs(R.string.bolus) + ": " + insulinAfterConstraints + "U\n" rMessage += rh.gs(R.string.carbs) + ": " + carbsAfterConstraints + "g" if (insulinAfterConstraints - insulin != 0.0 || carbsAfterConstraints - carbs != 0) { @@ -162,6 +168,7 @@ class ActionStringHandler @Inject constructor( rMessage += rh.gs(R.string.wear_action_tempt_preset_message, reason, activityTT, activityTTDuration) rAction = "temptarget $presetIsMGDL $activityTTDuration $activityTT $activityTT" } + "hypo" -> { val hypoTTDuration = defaultValueHelper.determineHypoTTDuration() val hypoTT = defaultValueHelper.determineHypoTT() @@ -169,6 +176,7 @@ class ActionStringHandler @Inject constructor( rMessage += rh.gs(R.string.wear_action_tempt_preset_message, reason, hypoTT, hypoTTDuration) rAction = "temptarget $presetIsMGDL $hypoTTDuration $hypoTT $hypoTT" } + "eating" -> { val eatingSoonTTDuration = defaultValueHelper.determineEatingSoonTTDuration() val eatingSoonTT = defaultValueHelper.determineEatingSoonTT() @@ -176,6 +184,7 @@ class ActionStringHandler @Inject constructor( rMessage += rh.gs(R.string.wear_action_tempt_preset_message, reason, eatingSoonTT, eatingSoonTTDuration) rAction = "temptarget $presetIsMGDL $eatingSoonTTDuration $eatingSoonTT $eatingSoonTT" } + else -> { sendError(rh.gs(R.string.wear_action_tempt_preset_error, preset)) return @@ -206,8 +215,8 @@ class ActionStringHandler @Inject constructor( sendError(rh.gs(R.string.wear_action_tempt_max_bg_error)) return } - rMessage += if(act[3] === act[4]) rh.gs(R.string.wear_action_tempt_manual_message, act[3], act[2]) - else rh.gs(R.string.wear_action_tempt_manual_range_message, act[3], act[4], act[2]) + rMessage += if (act[3] === act[4]) rh.gs(R.string.wear_action_tempt_manual_message, act[3], act[2]) + else rh.gs(R.string.wear_action_tempt_manual_range_message, act[3], act[4], act[2]) rAction = actionString } } @@ -227,10 +236,15 @@ class ActionStringHandler @Inject constructor( sendError("Update APP on Watch!") return } else if ("wizard2" == act[0]) { ////////////////////////////////////////////// WIZARD + val pump = activePlugin.activePump + if (!pump.isInitialized() || pump.isSuspended() || loop.isDisconnected) { + sendError(rh.gs(R.string.wizard_pump_not_available)) + return + } val carbsBeforeConstraints = SafeParse.stringToInt(act[1]) val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbsBeforeConstraints)).value() if (carbsAfterConstraints - carbsBeforeConstraints != 0) { - sendError("Carb constraint violation!") + sendError(rh.gs(R.string.wizard_carbs_constraint)) return } val useBG = sp.getBoolean(R.string.key_wearwizard_bg, true) @@ -243,80 +257,94 @@ class ActionStringHandler @Inject constructor( val profile = profileFunction.getProfile() val profileName = profileFunction.getProfileName() if (profile == null) { - sendError("No profile found!") + sendError(rh.gs(R.string.wizard_no_active_profile)) return } val bgReading = iobCobCalculator.ads.actualBg() if (bgReading == null) { - sendError("No recent BG to base calculation on!") + sendError(rh.gs(R.string.wizard_no_actual_bg)) return } val cobInfo = iobCobCalculator.getCobInfo(false, "Wizard wear") if (cobInfo.displayCob == null) { - sendError("Unknown COB! BG reading missing or recent app restart?") + sendError(rh.gs(R.string.wizard_no_cob)) return } - val format = DecimalFormat("0.00") - val formatInt = DecimalFormat("0") val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() val tempTarget = if (dbRecord is ValueWrapper.Existing) dbRecord.value else null - val bolusWizard = BolusWizard(injector).doCalc(profile, profileName, tempTarget, - carbsAfterConstraints, if (cobInfo.displayCob != null) cobInfo.displayCob!! else 0.0, bgReading.valueToUnits(profileFunction.getUnits()), - 0.0, percentage, useBG, useCOB, useBolusIOB, useBasalIOB, false, useTT, useTrend, false) - if (abs(bolusWizard.insulinAfterConstraints - bolusWizard.calculatedTotalInsulin) >= 0.01) { - sendError("Insulin constraint violation!" + - "\nCannot deliver " + format.format(bolusWizard.calculatedTotalInsulin) + "!") + val bolusWizard = BolusWizard(injector).doCalc( + profile, profileName, tempTarget, + carbsAfterConstraints, cobInfo.displayCob!!, bgReading.valueToUnits(profileFunction.getUnits()), + 0.0, percentage, useBG, useCOB, useBolusIOB, useBasalIOB, false, useTT, useTrend, false + ) + val insulinAfterConstraints = bolusWizard.insulinAfterConstraints + val minStep = pump.pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints) + if (abs(insulinAfterConstraints - bolusWizard.calculatedTotalInsulin) >= minStep) { + sendError(rh.gs(R.string.wizard_constraint_bolus_size, bolusWizard.calculatedTotalInsulin)) return } if (bolusWizard.calculatedTotalInsulin <= 0 && bolusWizard.carbs <= 0) { rAction = "info" - rTitle = "INFO" + rTitle = rh.gs(R.string.info) } else { rAction = actionString } - rMessage += "Carbs: " + bolusWizard.carbs + "g" - rMessage += "\nBolus: " + format.format(bolusWizard.calculatedTotalInsulin) + "U" + rMessage += rh.gs(R.string.wizard_result, bolusWizard.calculatedTotalInsulin, bolusWizard.carbs) rMessage += "\n_____________" - rMessage += "\nCalc (IC:" + DecimalFormatter.to1Decimal(bolusWizard.ic) + ", " + "ISF:" + DecimalFormatter.to1Decimal(bolusWizard.sens) + "): " - rMessage += "\nFrom Carbs: " + format.format(bolusWizard.insulinFromCarbs) + "U" - if (useCOB) rMessage += "\nFrom" + formatInt.format(cobInfo.displayCob) + "g COB : " + format.format(bolusWizard.insulinFromCOB) + "U" - if (useBG) rMessage += "\nFrom BG: " + format.format(bolusWizard.insulinFromBG) + "U" - if (useBolusIOB) rMessage += "\nBolus IOB: " + format.format(bolusWizard.insulinFromBolusIOB) + "U" - if (useBasalIOB) rMessage += "\nBasal IOB: " + format.format(bolusWizard.insulinFromBasalIOB) + "U" - if (useTrend) rMessage += "\nFrom 15' trend: " + format.format(bolusWizard.insulinFromTrend) + "U" - if (percentage != 100) { - rMessage += "\nPercentage: " + format.format(bolusWizard.totalBeforePercentageAdjustment) + "U * " + percentage + "% -> ~" + format.format(bolusWizard.calculatedTotalInsulin) + "U" - } + rMessage += "\n" + bolusWizard.explainShort() lastBolusWizard = bolusWizard } else if ("quick_wizard" == act[0]) { val guid = act[1] val actualBg = iobCobCalculator.ads.actualBg() val profile = profileFunction.getProfile() val profileName = profileFunction.getProfileName() - val pump = activePlugin.activePump val quickWizardEntry = quickWizard.get(guid) - Log.i("QuickWizard", "handleInitiate: quick_wizard " + quickWizardEntry?.buttonText() + " c "+ quickWizardEntry?.carbs()) - if (quickWizardEntry != null && actualBg != null && profile != null) { - // Logic related from Overview.kt - val wizard = quickWizardEntry.doCalc(profile, profileName, actualBg, true) - if (wizard.calculatedTotalInsulin > 0.0 && quickWizardEntry.carbs() > 0.0) { - val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(quickWizardEntry.carbs())).value() - val insulinAfterConstraints = wizard.insulinAfterConstraints - if (abs(insulinAfterConstraints - wizard.calculatedTotalInsulin) >= pump.pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints) || carbsAfterConstraints != quickWizardEntry.carbs()) { - // TODO check error is correct - sendError(rh.gs(R.string.constraints_violation) + "\n" + rh.gs(R.string.changeyourinput)) - return - } - rMessage = rh.gs(R.string.quick_wizard_message, quickWizardEntry.buttonText(), wizard.calculatedTotalInsulin, quickWizardEntry.carbs()) - rAction = "bolus $insulinAfterConstraints $carbsAfterConstraints" - Log.i("QuickWizard", "handleInitiate: quick_wizard action=$rAction") - } else { - sendError(rh.gs(R.string.quick_wizard_no_action)) - } - } else { - sendError(rh.gs(R.string.quick_wizard_can_not_calculate)) + Log.i("QuickWizard", "handleInitiate: quick_wizard " + quickWizardEntry?.buttonText() + " c " + quickWizardEntry?.carbs()) + if (quickWizardEntry == null) { + sendError(rh.gs(R.string.quick_wizard_not_available)) + return } + if (actualBg == null) { + sendError(rh.gs(R.string.wizard_no_actual_bg)) + return + } + if (profile == null) { + sendError(rh.gs(R.string.wizard_no_active_profile)) + return + } + val cobInfo = iobCobCalculator.getCobInfo(false, "QuickWizard wear") + if (cobInfo.displayCob == null) { + sendError(rh.gs(R.string.wizard_no_cob)) + return + } + val pump = activePlugin.activePump + if (!pump.isInitialized() || pump.isSuspended() || loop.isDisconnected) { + sendError(rh.gs(R.string.wizard_pump_not_available)) + return + } + + val wizard = quickWizardEntry.doCalc(profile, profileName, actualBg, true) + + val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(quickWizardEntry.carbs())).value() + if (carbsAfterConstraints != quickWizardEntry.carbs()) { + sendError(rh.gs(R.string.wizard_carbs_constraint)) + return + } + val insulinAfterConstraints = wizard.insulinAfterConstraints + val minStep = pump.pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints) + if (abs(insulinAfterConstraints - wizard.calculatedTotalInsulin) >= minStep) { + sendError(rh.gs(R.string.wizard_constraint_bolus_size, wizard.calculatedTotalInsulin)) + return + } + + rMessage = rh.gs(R.string.quick_wizard_message, quickWizardEntry.buttonText(), wizard.calculatedTotalInsulin, quickWizardEntry.carbs()) + rAction = "bolus $insulinAfterConstraints $carbsAfterConstraints" + Log.i("QuickWizard", "handleInitiate: quick_wizard action=$rAction") + + rMessage += "\n_____________" + rMessage += "\n" + wizard.explainShort() + } else if ("opencpp" == act[0]) { val activeProfileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet() if (activeProfileSwitch is ValueWrapper.Existing) { // read CPP values @@ -401,7 +429,7 @@ class ActionStringHandler @Inject constructor( wearPlugin.requestNotificationCancel(rAction) return } else { - sendError("Unknown action command: " + act[0] ) + sendError(rh.gs(R.string.wear_unknown_action_string) + act[0]) return } // send result @@ -632,39 +660,45 @@ class ActionStringHandler @Inject constructor( } //send profile to pump uel.log(Action.PROFILE_SWITCH, Sources.Wear, - ValueWithUnit.Percent(percentage), - ValueWithUnit.Hour(timeshift).takeIf { timeshift != 0 }) + ValueWithUnit.Percent(percentage), + ValueWithUnit.Hour(timeshift).takeIf { timeshift != 0 }) profileFunction.createProfileSwitch(0, percentage, timeshift) } private fun generateTempTarget(duration: Int, low: Double, high: Double) { if (duration != 0) { - disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction( - timestamp = System.currentTimeMillis(), - duration = TimeUnit.MINUTES.toMillis(duration.toLong()), - reason = TemporaryTarget.Reason.WEAR, - lowTarget = Profile.toMgdl(low, profileFunction.getUnits()), - highTarget = Profile.toMgdl(high, profileFunction.getUnits()) - )).subscribe({ result -> - result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") } - result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } - }, { - aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) - }) - uel.log(Action.TT, Sources.Wear, + disposable += repository.runTransactionForResult( + InsertAndCancelCurrentTemporaryTargetTransaction( + timestamp = System.currentTimeMillis(), + duration = TimeUnit.MINUTES.toMillis(duration.toLong()), + reason = TemporaryTarget.Reason.WEAR, + lowTarget = Profile.toMgdl(low, profileFunction.getUnits()), + highTarget = Profile.toMgdl(high, profileFunction.getUnits()) + ) + ).subscribe({ result -> + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) + }) + uel.log( + Action.TT, Sources.Wear, ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.WEAR), ValueWithUnit.fromGlucoseUnit(low, profileFunction.getUnits().asText), ValueWithUnit.fromGlucoseUnit(high, profileFunction.getUnits().asText).takeIf { low != high }, - ValueWithUnit.Minute(duration)) + ValueWithUnit.Minute(duration) + ) } else { disposable += repository.runTransactionForResult(CancelCurrentTemporaryTargetIfAnyTransaction(System.currentTimeMillis())) .subscribe({ result -> - result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } - }, { - aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) - }) - uel.log(Action.CANCEL_TT, Sources.Wear, - ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.WEAR)) + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) + }) + uel.log( + Action.CANCEL_TT, Sources.Wear, + ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.WEAR) + ) } } @@ -673,13 +707,15 @@ class ActionStringHandler @Inject constructor( detailedBolusInfo.insulin = amount detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.PRIMING uel.log(Action.PRIME_BOLUS, Sources.Wear, - ValueWithUnit.Insulin(amount).takeIf { amount != 0.0 }) + ValueWithUnit.Insulin(amount).takeIf { amount != 0.0 }) commandQueue.bolus(detailedBolusInfo, object : Callback() { override fun run() { if (!result.success) { - sendError(rh.gs(R.string.treatmentdeliveryerror) + - "\n" + - result.comment) + sendError( + rh.gs(R.string.treatmentdeliveryerror) + + "\n" + + result.comment + ) } } }) @@ -687,9 +723,9 @@ class ActionStringHandler @Inject constructor( private fun doECarbs(carbs: Int, time: Long, duration: Int) { uel.log(if (duration == 0) Action.CARBS else Action.EXTENDED_CARBS, Sources.Wear, - ValueWithUnit.Timestamp(time), - ValueWithUnit.Gram(carbs), - ValueWithUnit.Hour(duration).takeIf { duration != 0 }) + ValueWithUnit.Timestamp(time), + ValueWithUnit.Gram(carbs), + ValueWithUnit.Hour(duration).takeIf { duration != 0 }) doBolus(0.0, carbs, time, duration) } @@ -708,15 +744,17 @@ class ActionStringHandler @Inject constructor( else -> Action.TREATMENT } uel.log(action, Sources.Wear, - ValueWithUnit.Insulin(amount).takeIf { amount != 0.0 }, - ValueWithUnit.Gram(carbs).takeIf { carbs != 0 }, - ValueWithUnit.Hour(carbsDuration).takeIf { carbsDuration != 0 }) + ValueWithUnit.Insulin(amount).takeIf { amount != 0.0 }, + ValueWithUnit.Gram(carbs).takeIf { carbs != 0 }, + ValueWithUnit.Hour(carbsDuration).takeIf { carbsDuration != 0 }) commandQueue.bolus(detailedBolusInfo, object : Callback() { override fun run() { if (!result.success) { - sendError(rh.gs(R.string.treatmentdeliveryerror) + - "\n" + - result.comment) + sendError( + rh.gs(R.string.treatmentdeliveryerror) + + "\n" + + result.comment + ) } } }) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt b/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt index 5a66cd2510..dbc0ddf651 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt @@ -18,6 +18,8 @@ import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.transactions.InsertOrUpdateBolusCalculatorResultTransaction import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.extensions.formatColor +import info.nightscout.androidaps.extensions.highValueToUnitsToString +import info.nightscout.androidaps.extensions.lowValueToUnitsToString import info.nightscout.androidaps.interfaces.* import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag @@ -135,27 +137,28 @@ class BolusWizard @Inject constructor( private var quickWizard: Boolean = true var usePercentage: Boolean = false - fun doCalc(profile: Profile, - profileName: String, - tempTarget: TemporaryTarget?, - carbs: Int, - cob: Double, - bg: Double, - correction: Double, - percentageCorrection: Int = 100, - useBg: Boolean, - useCob: Boolean, - includeBolusIOB: Boolean, - includeBasalIOB: Boolean, - useSuperBolus: Boolean, - useTT: Boolean, - useTrend: Boolean, - useAlarm: Boolean, - notes: String = "", - carbTime: Int = 0, - usePercentage: Boolean = false, - totalPercentage: Double = 100.0, - quickWizard: Boolean = false + fun doCalc( + profile: Profile, + profileName: String, + tempTarget: TemporaryTarget?, + carbs: Int, + cob: Double, + bg: Double, + correction: Double, + percentageCorrection: Int = 100, + useBg: Boolean, + useCob: Boolean, + includeBolusIOB: Boolean, + includeBasalIOB: Boolean, + useSuperBolus: Boolean, + useTT: Boolean, + useTrend: Boolean, + useAlarm: Boolean, + notes: String = "", + carbTime: Int = 0, + usePercentage: Boolean = false, + totalPercentage: Double = 100.0, + quickWizard: Boolean = false ): BolusWizard { this.profile = profile @@ -314,7 +317,9 @@ class BolusWizard @Inject constructor( actions.add(rh.gs(R.string.carbs) + ": " + rh.gs(R.string.format_carbs, carbs).formatColor(rh, R.color.carbs) + timeShift) } if (insulinFromCOB > 0) { - actions.add(rh.gs(R.string.cobvsiob) + ": " + rh.gs(R.string.formatsignedinsulinunits, insulinFromBolusIOB + insulinFromBasalIOB + insulinFromCOB + insulinFromBG).formatColor(rh, R.color.cobAlert)) + actions.add( + rh.gs(R.string.cobvsiob) + ": " + rh.gs(R.string.formatsignedinsulinunits, insulinFromBolusIOB + insulinFromBasalIOB + insulinFromCOB + insulinFromBG).formatColor(rh, R.color.cobAlert) + ) val absorptionRate = iobCobCalculator.ads.slowAbsorptionPercentage(60) if (absorptionRate > .25) actions.add(rh.gs(R.string.slowabsorptiondetected, rh.gc(R.color.cobAlert), (absorptionRate * 100).toInt())) @@ -344,8 +349,8 @@ class BolusWizard @Inject constructor( carbTimer.removeEatReminder() if (sp.getBoolean(R.string.key_usebolusadvisor, false) && Profile.toMgdl(bg, profile.units) > 180 && carbs > 0 && carbTime >= 0) OKDialog.showYesNoCancel(ctx, rh.gs(R.string.bolusadvisor), rh.gs(R.string.bolusadvisormessage), - { bolusAdvisorProcessing(ctx) }, - { commonProcessing(ctx) } + { bolusAdvisorProcessing(ctx) }, + { commonProcessing(ctx) } ) else commonProcessing(ctx) @@ -367,10 +372,13 @@ class BolusWizard @Inject constructor( carbTime = 0 bolusCalculatorResult = createBolusCalculatorResult() notes = this@BolusWizard.notes - uel.log(Action.BOLUS_ADVISOR, if (quickWizard) Sources.QuickWizard else Sources.WizardDialog, + uel.log( + Action.BOLUS_ADVISOR, + if (quickWizard) Sources.QuickWizard else Sources.WizardDialog, notes, ValueWithUnit.TherapyEventType(eventType.toDBbEventType()), - ValueWithUnit.Insulin(insulinAfterConstraints)) + ValueWithUnit.Insulin(insulinAfterConstraints) + ) if (insulin > 0) { commandQueue.bolus(this, object : Callback() { override fun run() { @@ -385,6 +393,26 @@ class BolusWizard @Inject constructor( }) } + fun explainShort(): String { + var message = rh.gs(R.string.wizard_explain_calc, ic, sens) + message += "\n" + rh.gs(R.string.wizard_explain_carbs, insulinFromCarbs) + if (useTT && tempTarget != null) { + val tt = if (tempTarget?.lowTarget == tempTarget?.highTarget) tempTarget?.lowValueToUnitsToString(profile.units) + else rh.gs(R.string.wizard_explain_tt_to, tempTarget?.lowValueToUnitsToString(profile.units), tempTarget?.highValueToUnitsToString(profile.units)) + message += "\n" + rh.gs(R.string.wizard_explain_tt, tt) + } + if (useCob) message += "\n" + rh.gs(R.string.wizard_explain_cob, cob, insulinFromCOB) + if (useBg) message += "\n" + rh.gs(R.string.wizard_explain_bg, insulinFromBG) + if (includeBolusIOB) message += "\n" + rh.gs(R.string.wizard_explain_bolus_iob, insulinFromBolusIOB) + if (includeBasalIOB) message += "\n" + rh.gs(R.string.wizard_explain_basal_iob, insulinFromBasalIOB) + if (usePercentage) message += "\n" + rh.gs(R.string.wizard_explain_trend, insulinFromTrend) + if (useSuperBolus) message += "\n" + rh.gs(R.string.wizard_explain_superbolus, insulinFromSuperBolus) + if (usePercentage) { + message += "\n" + rh.gs(R.string.wizard_explain_percent, totalBeforePercentageAdjustment, percentageCorrection, calculatedTotalInsulin) + } + return message + } + private fun commonProcessing(ctx: Context) { val profile = profileFunction.getProfile() ?: return val pump = activePlugin.activePump @@ -433,17 +461,17 @@ class BolusWizard @Inject constructor( bolusCalculatorResult = createBolusCalculatorResult() notes = this@BolusWizard.notes if (insulin > 0 || carbs > 0) { - val action = when { + val action = when { insulinAfterConstraints.equals(0.0) -> Action.CARBS carbs.equals(0.0) -> Action.BOLUS else -> Action.TREATMENT } uel.log(action, if (quickWizard) Sources.QuickWizard else Sources.WizardDialog, - notes, - ValueWithUnit.TherapyEventType(eventType.toDBbEventType()), - ValueWithUnit.Insulin(insulinAfterConstraints).takeIf { insulinAfterConstraints != 0.0 }, - ValueWithUnit.Gram(this@BolusWizard.carbs).takeIf { this@BolusWizard.carbs != 0 }, - ValueWithUnit.Minute(carbTime).takeIf { carbTime != 0 }) + notes, + ValueWithUnit.TherapyEventType(eventType.toDBbEventType()), + ValueWithUnit.Insulin(insulinAfterConstraints).takeIf { insulinAfterConstraints != 0.0 }, + ValueWithUnit.Gram(this@BolusWizard.carbs).takeIf { this@BolusWizard.carbs != 0 }, + ValueWithUnit.Minute(carbTime).takeIf { carbTime != 0 }) commandQueue.bolus(this, object : Callback() { override fun run() { if (!result.success) { @@ -469,9 +497,9 @@ class BolusWizard @Inject constructor( private fun calcPercentageWithConstraints() { calculatedPercentage = 100.0 if (totalBeforePercentageAdjustment != insulinFromCorrection) - calculatedPercentage = calculatedTotalInsulin/(totalBeforePercentageAdjustment-insulinFromCorrection) * 100 + calculatedPercentage = calculatedTotalInsulin / (totalBeforePercentageAdjustment - insulinFromCorrection) * 100 calculatedPercentage = max(calculatedPercentage, 10.0) - calculatedPercentage = min(calculatedPercentage,250.0) + calculatedPercentage = min(calculatedPercentage, 250.0) } private fun calcCorrectionWithConstraints() { @@ -481,4 +509,4 @@ class BolusWizard @Inject constructor( calculatedCorrection = max(-constraintChecker.getMaxBolusAllowed().value(), calculatedCorrection) } -} \ No newline at end of file +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d81abd4254..78f167ea3d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1175,9 +1175,26 @@ Temptarget:\nMin: %1$s\nMax: %2$s\nDuration: %3$s Temptarget:\nTarget: %1$s\nDuration: %2$s Temptarget:\Reason: %1$s\nTarget: %2$s\nDuration: %3$s - No insulin needed nor are carbs added. - Can not calculate wizard, requires actual blood glucose and active profile. - Quick Wizard: %1$s\nInsulin: %2$.2fU\nCarbs: %3$dg + QuickWizard: %1$s\nInsulin: %2$.2fU\nCarbs: %3$dg + Calc. Wizard:\nInsulin: %1$.2fU\nCarbs: %2$dg Show entry on device: - + Selected quickwizard no longer available, please refresh your tile + No recent BG to base calculation on! + No active profile set! + Unknown COB! BG reading missing or recent app restart? + Carb constraint violation! + Calc (IC: %2$.1f, ISF: %2$.1f) from:" + Carbs: %1$.2fU + COB: %1$.0fg %2$.2fU + BG: %1$.2fU + Basal IOB: %1$.2fU + Bolus IOB: %1$.2fU + Superbolus: %1$.2fU + 15\' trend: %1$.2fU + Perctage: %1$.2fU x %2$d% = %3$.2fU + Insulin constraint violation!\nCannot deliver %1$.2fU + TempT: %1$s + %1$s to %2$s + No pump available! + Unknown action command: diff --git a/wear/src/main/java/info/nightscout/androidaps/tile/ActionSource.kt b/wear/src/main/java/info/nightscout/androidaps/tile/ActionSource.kt index a495d3ddf8..d9f87ad77f 100644 --- a/wear/src/main/java/info/nightscout/androidaps/tile/ActionSource.kt +++ b/wear/src/main/java/info/nightscout/androidaps/tile/ActionSource.kt @@ -8,7 +8,7 @@ import info.nightscout.androidaps.interaction.actions.ECarbActivity import info.nightscout.androidaps.interaction.actions.TempTargetActivity import info.nightscout.androidaps.interaction.actions.WizardActivity -object ActionSource : StaticTileSource(), TileSource { +object ActionSource : StaticTileSource() { override val preferencePrefix = "tile_action_" diff --git a/wear/src/main/java/info/nightscout/androidaps/tile/QuickWizardSource.kt b/wear/src/main/java/info/nightscout/androidaps/tile/QuickWizardSource.kt index d241ac0b9a..36e2f224fb 100644 --- a/wear/src/main/java/info/nightscout/androidaps/tile/QuickWizardSource.kt +++ b/wear/src/main/java/info/nightscout/androidaps/tile/QuickWizardSource.kt @@ -21,7 +21,6 @@ object QuickWizardSource : TileSource { val validFrom = quick.getInt("from", 0) val validTo = quick.getInt("to", 0) val isActive = sfm in validFrom..validTo - // use from and to to schedule new update for timeline, for now just refresh every minute val guid = quick.getString("guid", "") if (isActive && guid != "") { quickList.add( @@ -44,6 +43,36 @@ object QuickWizardSource : TileSource { return quickList } + override fun getValidFor(context: Context): Long? { + val quickMap = getDataMap(context) + if (quickMap.size == 0) { + return null + } + val sfm = secondsFromMidnight() + var validTill = 24 * 60 * 60 + + for (quick in quickMap) { + val validFrom = quick.getInt("from", 0) + val validTo = quick.getInt("to", 0) + val isActive = sfm in validFrom..validTo + val guid = quick.getString("guid", "") + Log.i(TAG, "valid: " + validFrom + "-" + validTo) + if (guid != "") { + if (isActive && validTill > validTo) { + validTill = validTo + } + if (validFrom > sfm && validTill > validFrom) { + validTill = validFrom + } + } + } + + val validWithin = 60 + val delta = (validTill - sfm + validWithin) * 1000L + Log.i(TAG, "getValidTill: sfm" + sfm + " till" + validTill + " d=" + delta) + return delta + } + private fun getDataMap(context: Context): ArrayList { val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context) val key = context.resources.getString(R.string.key_quick_wizard_data_map) diff --git a/wear/src/main/java/info/nightscout/androidaps/tile/StaticTileSource.kt b/wear/src/main/java/info/nightscout/androidaps/tile/StaticTileSource.kt index bbd7be1a91..c1f369275c 100644 --- a/wear/src/main/java/info/nightscout/androidaps/tile/StaticTileSource.kt +++ b/wear/src/main/java/info/nightscout/androidaps/tile/StaticTileSource.kt @@ -16,14 +16,14 @@ class StaticAction( message: String? = null, ) : Action(buttonText, buttonTextSub, activityClass, iconRes, actionString, message) -abstract class StaticTileSource { +abstract class StaticTileSource : TileSource { abstract fun getActions(resources: Resources): List abstract val preferencePrefix: String abstract fun getDefaultConfig(): Map - open fun getSelectedActions(context: Context): List { + override fun getSelectedActions(context: Context): List { val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context) setDefaultSettings(sharedPrefs) @@ -40,12 +40,14 @@ abstract class StaticTileSource { return actionList } + override fun getValidFor(context: Context): Long? = null + private fun getActionFromPreference(resources: Resources, sharedPrefs: SharedPreferences, index: Int): Action? { val actionPref = sharedPrefs.getString(preferencePrefix + index, "none") return getActions(resources).find { action -> action.settingName == actionPref } } - open fun setDefaultSettings(sharedPrefs: SharedPreferences) { + private fun setDefaultSettings(sharedPrefs: SharedPreferences) { val defaults = getDefaultConfig() val firstKey = defaults.firstNotNullOf { settings -> settings.key } if (!sharedPrefs.contains(firstKey)) { @@ -56,4 +58,5 @@ abstract class StaticTileSource { editor.apply() } } + } diff --git a/wear/src/main/java/info/nightscout/androidaps/tile/TempTargetSource.kt b/wear/src/main/java/info/nightscout/androidaps/tile/TempTargetSource.kt index 37d4f55e1c..75cdbca36c 100644 --- a/wear/src/main/java/info/nightscout/androidaps/tile/TempTargetSource.kt +++ b/wear/src/main/java/info/nightscout/androidaps/tile/TempTargetSource.kt @@ -5,8 +5,9 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.interaction.actions.BackgroundActionActivity import info.nightscout.androidaps.interaction.actions.TempTargetActivity -object TempTargetSource : StaticTileSource(), TileSource { - override val preferencePrefix= "tile_tempt_" +object TempTargetSource : StaticTileSource() { + + override val preferencePrefix = "tile_tempt_" override fun getActions(resources: Resources): List { val message = resources.getString(R.string.action_tempt_confirmation) diff --git a/wear/src/main/java/info/nightscout/androidaps/tile/TileBase.kt b/wear/src/main/java/info/nightscout/androidaps/tile/TileBase.kt index 5c86a89f84..031dbf223e 100644 --- a/wear/src/main/java/info/nightscout/androidaps/tile/TileBase.kt +++ b/wear/src/main/java/info/nightscout/androidaps/tile/TileBase.kt @@ -45,6 +45,7 @@ interface TileSource { fun getResourceReferences(resources: android.content.res.Resources): List fun getSelectedActions(context: Context): List + fun getValidFor(context: Context): Long? } open class Action( @@ -73,8 +74,7 @@ abstract class TileBase : TileService() { ): ListenableFuture = serviceScope.future { val actionsSelected = getSelectedActions() val wearControl = getWearControl() - - Tile.Builder() + val tile = Tile.Builder() .setResourcesVersion(resourceVersion) .setTimeline( Timeline.Builder().addTimelineEntry( @@ -83,7 +83,12 @@ abstract class TileBase : TileService() { ).build() ).build() ) - .build() + + val validFor = validFor() + if (validFor != null) { + tile.setFreshnessIntervalMillis(validFor) + } + tile.build() } private fun getSelectedActions(): List { @@ -91,6 +96,10 @@ abstract class TileBase : TileService() { return source.getSelectedActions(this) } + private fun validFor(): Long? { + return source.getValidFor(this) + } + @RequiresApi(Build.VERSION_CODES.N) override fun onResourcesRequest( requestParams: ResourcesRequest