This commit is contained in:
Milos Kozak 2021-11-25 11:47:46 +01:00
commit a00dea8c0d
77 changed files with 1124 additions and 1075 deletions

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.activities
import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.os.Bundle
import android.text.Editable
@ -168,7 +169,8 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
binding.basalpctfromtdd.setParams(32.0, 32.0, 37.0, 1.0, DecimalFormat("0"), false, null)
binding.tdds.text = getString(R.string.tdd) + ": " + rh.gs(R.string.calculation_in_progress);
@SuppressLint("SetTextI18n")
binding.tdds.text = getString(R.string.tdd) + ": " + rh.gs(R.string.calculation_in_progress)
Thread {
val tdds = tddCalculator.stats()
runOnUiThread { binding.tdds.text = tdds }

View file

@ -76,7 +76,7 @@ class CalibrationDialog : DialogFragmentWithDate() {
val units = profileFunction.getUnits()
val unitLabel = if (units == GlucoseUnit.MMOL) rh.gs(R.string.mmol) else rh.gs(R.string.mgdl)
val actions: LinkedList<String?> = LinkedList()
val bg = binding.bg.value ?: return false
val bg = binding.bg.value
actions.add(rh.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, bg) + " " + unitLabel)
if (bg > 0) {
activity?.let { activity ->

View file

@ -189,7 +189,7 @@ class CarbsDialog : DialogFragmentWithDate() {
override fun submit(): Boolean {
if (_binding == null) return false
val carbs = binding.carbs.value?.toInt() ?: return false
val carbs = binding.carbs.value.toInt()
val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
val units = profileFunction.getUnits()
val activityTTDuration = defaultValueHelper.determineActivityTTDuration()

View file

@ -79,7 +79,7 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
override fun submit(): Boolean {
if (_binding == null) return false
val insulin = SafeParse.stringToDouble(binding.insulin.text ?: return false)
val insulin = SafeParse.stringToDouble(binding.insulin.text)
val durationInMinutes = binding.duration.value.toInt()
val actions: LinkedList<String> = LinkedList()
val insulinAfterConstraint = constraintChecker.applyExtendedBolusConstraints(Constraint(insulin)).value()

View file

@ -106,7 +106,7 @@ class FillDialog : DialogFragmentWithDate() {
override fun submit(): Boolean {
if (_binding == null) return false
val insulin = SafeParse.stringToDouble(binding.fillInsulinamount.text ?: return false)
val insulin = SafeParse.stringToDouble(binding.fillInsulinamount.text)
val actions: LinkedList<String?> = LinkedList()
val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value()

View file

@ -149,7 +149,7 @@ class InsulinDialog : DialogFragmentWithDate() {
override fun submit(): Boolean {
if (_binding == null) return false
val pumpDescription = activePlugin.activePump.pumpDescription
val insulin = SafeParse.stringToDouble(binding.amount.text ?: return false)
val insulin = SafeParse.stringToDouble(binding.amount.text)
val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value()
val actions: LinkedList<String?> = LinkedList()
val units = profileFunction.getUnits()

View file

@ -162,7 +162,7 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
?: return false
val actions: LinkedList<String> = LinkedList()
val duration = binding.duration.value?.toInt() ?: return false
val duration = binding.duration.value.toInt()
if (duration > 0L)
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, duration))
val profileName = binding.profile.selectedItem.toString()

View file

@ -47,8 +47,8 @@ class TempBasalDialog : DialogFragmentWithDate() {
override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("duration", binding.duration.value)
savedInstanceState.putDouble("basalpercentinput", binding.basalpercentinput.value)
savedInstanceState.putDouble("basalabsoluteinput", binding.basalabsoluteinput.value)
savedInstanceState.putDouble("basalPercentInput", binding.basalPercentInput.value)
savedInstanceState.putDouble("basalAbsoluteInput", binding.basalAbsoluteInput.value)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
@ -67,10 +67,10 @@ class TempBasalDialog : DialogFragmentWithDate() {
val maxTempPercent = pumpDescription.maxTempPercent.toDouble()
val tempPercentStep = pumpDescription.tempPercentStep.toDouble()
binding.basalpercentinput.setParams(savedInstanceState?.getDouble("basalpercentinput")
binding.basalPercentInput.setParams(savedInstanceState?.getDouble("basalPercentInput")
?: 100.0, 0.0, maxTempPercent, tempPercentStep, DecimalFormat("0"), true, binding.okcancel.ok)
binding.basalabsoluteinput.setParams(savedInstanceState?.getDouble("basalabsoluteinput")
binding.basalAbsoluteInput.setParams(savedInstanceState?.getDouble("basalAbsoluteInput")
?: profile.getBasal(), 0.0, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, DecimalFormat("0.00"), true, binding.okcancel.ok)
val tempDurationStep = pumpDescription.tempDurationStep.toDouble()
@ -97,17 +97,17 @@ class TempBasalDialog : DialogFragmentWithDate() {
if (_binding == null) return false
var percent = 0
var absolute = 0.0
val durationInMinutes = binding.duration.value?.toInt() ?: return false
val durationInMinutes = binding.duration.value.toInt()
val profile = profileFunction.getProfile() ?: return false
val actions: LinkedList<String> = LinkedList()
if (isPercentPump) {
val basalPercentInput = SafeParse.stringToInt(binding.basalpercentinput.text)
val basalPercentInput = SafeParse.stringToInt(binding.basalPercentInput.text)
percent = constraintChecker.applyBasalPercentConstraints(Constraint(basalPercentInput), profile).value()
actions.add(rh.gs(R.string.tempbasal_label) + ": $percent%")
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes))
if (percent != basalPercentInput) actions.add(rh.gs(R.string.constraintapllied))
} else {
val basalAbsoluteInput = SafeParse.stringToDouble(binding.basalabsoluteinput.text)
val basalAbsoluteInput = SafeParse.stringToDouble(binding.basalAbsoluteInput.text)
absolute = constraintChecker.applyBasalConstraints(Constraint(basalAbsoluteInput), profile).value()
actions.add(rh.gs(R.string.tempbasal_label) + ": " + rh.gs(R.string.pump_basebasalrate, absolute))
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes))

View file

@ -115,7 +115,7 @@ class TreatmentDialog : DialogFragmentWithDate() {
override fun submit(): Boolean {
if (_binding == null) return false
val pumpDescription = activePlugin.activePump.pumpDescription
val insulin = SafeParse.stringToDouble(binding.insulin.text ?: return false)
val insulin = SafeParse.stringToDouble(binding.insulin.text)
val carbs = SafeParse.stringToInt(binding.carbs.text)
val recordOnlyChecked = binding.recordOnly.isChecked
val actions: LinkedList<String?> = LinkedList()
@ -139,7 +139,7 @@ class TreatmentDialog : DialogFragmentWithDate() {
OKDialog.showConfirmation(activity, rh.gs(R.string.overview_treatment_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
val action = when {
insulinAfterConstraints.equals(0.0) -> Action.CARBS
carbsAfterConstraints.equals(0) -> Action.BOLUS
carbsAfterConstraints == 0 -> Action.BOLUS
else -> Action.TREATMENT
}
val detailedBolusInfo = DetailedBolusInfo()

View file

@ -258,10 +258,7 @@ class WizardDialog : DaggerDialogFragment() {
val units = profileFunction.getUnits()
binding.bgunits.text = units.asText
if (units == GlucoseUnit.MGDL)
binding.bgInput.setStep(1.0)
else
binding.bgInput.setStep(0.1)
binding.bgInput.step = if (units == GlucoseUnit.MGDL) 1.0 else 0.1
// Set BG if not old
binding.bgInput.value = iobCobCalculator.ads.actualBg()?.valueToUnits(units) ?: 0.0

View file

@ -109,7 +109,7 @@ class ObjectivesExamDialog : DaggerDialogFragment() {
task.answered = result
if (!result) {
task.disabledTo = dateUtil.now() + T.hours(1).msecs()
ToastUtils.showToastInUiThread(context, R.string.wronganswer)
context?.let { it1 -> ToastUtils.showToastInUiThread(it1, R.string.wronganswer) }
} else task.disabledTo = 0
updateGui()
rxBus.send(EventObjectivesUpdateGui())

View file

@ -646,9 +646,9 @@ class OverviewData @Inject constructor(
maxCobValueFound = max(maxCobValueFound, cob.toDouble())
lastCob = cob
}
if (autosensData.failoverToMinAbsorbtionRate) {
autosensData.setScale(cobScale)
autosensData.setChartTime(time)
if (autosensData.failOverToMinAbsorptionRate) {
autosensData.scale = cobScale
autosensData.chartTime = time
minFailOverActiveList.add(autosensData)
}
}

View file

@ -20,18 +20,21 @@ class BolusDataPoint @Inject constructor(
override fun getX(): Double = data.timestamp.toDouble()
override fun getY(): Double = if (data.type == Bolus.Type.SMB) defaultValueHelper.determineLowLine() else yValue
override fun getLabel(): String = DecimalFormatter.toPumpSupportedBolus(data.amount, activePlugin.activePump, rh)
override fun getDuration(): Long = 0
override fun getSize(): Float = 2f
override val label
get() = DecimalFormatter.toPumpSupportedBolus(data.amount, activePlugin.activePump, rh)
override val duration = 0L
override val size = 2f
override fun getShape(): PointsWithLabelGraphSeries.Shape =
if (data.type == Bolus.Type.SMB) PointsWithLabelGraphSeries.Shape.SMB
else PointsWithLabelGraphSeries.Shape.BOLUS
override val shape
get() = if (data.type == Bolus.Type.SMB) PointsWithLabelGraphSeries.Shape.SMB else PointsWithLabelGraphSeries.Shape.BOLUS
override fun getColor(): Int =
if (data.type == Bolus.Type.SMB) rh.gc(R.color.tempbasal)
else if (data.isValid) Color.CYAN
else rh.gc(android.R.color.holo_red_light)
override val color
get() =
when {
data.type == Bolus.Type.SMB -> rh.gc(R.color.tempbasal)
data.isValid -> Color.CYAN
else -> rh.gc(android.R.color.holo_red_light)
}
override fun setY(y: Double) {
yValue = y

View file

@ -14,15 +14,11 @@ class CarbsDataPoint @Inject constructor(
override fun getX(): Double = data.timestamp.toDouble()
override fun getY(): Double = yValue
override fun getLabel(): String = rh.gs(R.string.format_carbs, data.amount.toInt())
override fun getDuration(): Long = 0
override fun getSize(): Float = 2f
override fun getShape(): PointsWithLabelGraphSeries.Shape = PointsWithLabelGraphSeries.Shape.CARBS
override fun getColor(): Int =
if (data.isValid) rh.gc(R.color.carbs)
else rh.gc(android.R.color.holo_red_light)
override val label get() = rh.gs(R.string.format_carbs, data.amount.toInt())
override val duration = 0L
override val size = 2f
override val shape = PointsWithLabelGraphSeries.Shape.CARBS
override val color get() = if (data.isValid) rh.gc(R.color.carbs) else rh.gc(android.R.color.holo_red_light)
override fun setY(y: Double) {
yValue = y

View file

@ -17,9 +17,9 @@ class EffectiveProfileSwitchDataPoint @Inject constructor(
yValue = y
}
override fun getLabel(): String = data.originalCustomizedName
override fun getDuration(): Long = 0
override fun getShape(): PointsWithLabelGraphSeries.Shape = PointsWithLabelGraphSeries.Shape.PROFILE
override fun getSize(): Float = 10f
override fun getColor(): Int = Color.CYAN
override val label get() = data.originalCustomizedName
override val duration = 0L
override val shape = PointsWithLabelGraphSeries.Shape.PROFILE
override val size = 10f
override val color = Color.CYAN
}

View file

@ -13,11 +13,11 @@ class ExtendedBolusDataPoint @Inject constructor(
override fun getX(): Double = data.timestamp.toDouble()
override fun getY(): Double = yValue
override fun getLabel(): String = data.toStringTotal()
override fun getDuration(): Long = data.duration
override fun getSize(): Float = 10f
override fun getShape(): PointsWithLabelGraphSeries.Shape = PointsWithLabelGraphSeries.Shape.EXTENDEDBOLUS
override fun getColor(): Int = Color.CYAN
override val label get() = data.toStringTotal()
override val duration get() = data.duration
override val size = 10f
override val shape = PointsWithLabelGraphSeries.Shape.EXTENDEDBOLUS
override val color = Color.CYAN
override fun setY(y: Double) {
yValue = y

View file

@ -19,44 +19,36 @@ class GlucoseValueDataPoint @Inject constructor(
fun valueToUnits(units: GlucoseUnit): Double =
if (units == GlucoseUnit.MGDL) data.value else data.value * Constants.MGDL_TO_MMOLL
override fun getX(): Double {
return data.timestamp.toDouble()
}
override fun getY(): Double {
return valueToUnits(profileFunction.getUnits())
}
override fun getX(): Double = data.timestamp.toDouble()
override fun getY(): Double = valueToUnits(profileFunction.getUnits())
override fun setY(y: Double) {}
override fun getLabel(): String? = null
override fun getDuration(): Long = 0
override fun getShape(): PointsWithLabelGraphSeries.Shape =
if (isPrediction) PointsWithLabelGraphSeries.Shape.PREDICTION
else PointsWithLabelGraphSeries.Shape.BG
override fun getSize(): Float = 1f
override fun getColor(): Int {
val units = profileFunction.getUnits()
val lowLine = defaultValueHelper.determineLowLine()
val highLine = defaultValueHelper.determineHighLine()
return when {
isPrediction -> predictionColor
valueToUnits(units) < lowLine -> rh.gc(R.color.low)
valueToUnits(units) > highLine -> rh.gc(R.color.high)
else -> rh.gc(R.color.inrange)
override val label: String? = null
override val duration = 0L
override val shape get() = if (isPrediction) PointsWithLabelGraphSeries.Shape.PREDICTION else PointsWithLabelGraphSeries.Shape.BG
override val size = 1f
override val color: Int
get() {
val units = profileFunction.getUnits()
val lowLine = defaultValueHelper.determineLowLine()
val highLine = defaultValueHelper.determineHighLine()
return when {
isPrediction -> predictionColor
valueToUnits(units) < lowLine -> rh.gc(R.color.low)
valueToUnits(units) > highLine -> rh.gc(R.color.high)
else -> rh.gc(R.color.inrange)
}
}
}
val predictionColor: Int
get() {
return when (data.sourceSensor) {
GlucoseValue.SourceSensor.IOB_PREDICTION -> rh.gc(R.color.iob)
GlucoseValue.SourceSensor.IOB_PREDICTION -> rh.gc(R.color.iob)
GlucoseValue.SourceSensor.COB_PREDICTION -> rh.gc(R.color.cob)
GlucoseValue.SourceSensor.A_COB_PREDICTION -> -0x7f000001 and rh.gc(R.color.cob)
GlucoseValue.SourceSensor.UAM_PREDICTION -> rh.gc(R.color.uam)
GlucoseValue.SourceSensor.ZT_PREDICTION -> rh.gc(R.color.zt)
else -> R.color.white
GlucoseValue.SourceSensor.ZT_PREDICTION -> rh.gc(R.color.zt)
else -> R.color.white
}
}

View file

@ -20,9 +20,9 @@ class InMemoryGlucoseValueDataPoint @Inject constructor(
override fun getX(): Double = data.timestamp.toDouble()
override fun getY(): Double = valueToUnits(profileFunction.getUnits())
override fun setY(y: Double) {}
override fun getLabel(): String? = null
override fun getDuration(): Long = 0
override fun getShape(): PointsWithLabelGraphSeries.Shape = PointsWithLabelGraphSeries.Shape.BUCKETED_BG
override fun getSize(): Float = 0.3f
override fun getColor(): Int = rh.gc(R.color.white)
override val label: String? = null
override val duration = 0L
override val shape = PointsWithLabelGraphSeries.Shape.BUCKETED_BG
override val size = 0.3f
override val color get() = rh.gc(R.color.white)
}

View file

@ -19,9 +19,7 @@ class TherapyEventDataPoint @Inject constructor(
private var yValue = 0.0
override fun getX(): Double {
return data.timestamp.toDouble()
}
override fun getX(): Double = data.timestamp.toDouble()
override fun getY(): Double {
val units = profileFunction.getUnits()
@ -46,30 +44,29 @@ class TherapyEventDataPoint @Inject constructor(
yValue = y
}
override fun getLabel(): String? =
if (data.note.isNullOrBlank().not()) data.note
else translator.translate(data.type)
override val label get() = if (data.note.isNullOrBlank().not()) data.note else translator.translate(data.type)
override val duration get() = data.duration
override val shape
get() =
when {
data.type == TherapyEvent.Type.NS_MBG -> PointsWithLabelGraphSeries.Shape.MBG
data.type == TherapyEvent.Type.FINGER_STICK_BG_VALUE -> PointsWithLabelGraphSeries.Shape.BGCHECK
data.type == TherapyEvent.Type.ANNOUNCEMENT -> PointsWithLabelGraphSeries.Shape.ANNOUNCEMENT
data.type == TherapyEvent.Type.APS_OFFLINE -> PointsWithLabelGraphSeries.Shape.OPENAPSOFFLINE
data.type == TherapyEvent.Type.EXERCISE -> PointsWithLabelGraphSeries.Shape.EXERCISE
duration > 0 -> PointsWithLabelGraphSeries.Shape.GENERALWITHDURATION
else -> PointsWithLabelGraphSeries.Shape.GENERAL
}
override fun getDuration(): Long = data.duration
override fun getShape(): PointsWithLabelGraphSeries.Shape =
when {
data.type == TherapyEvent.Type.NS_MBG -> PointsWithLabelGraphSeries.Shape.MBG
data.type == TherapyEvent.Type.FINGER_STICK_BG_VALUE -> PointsWithLabelGraphSeries.Shape.BGCHECK
data.type == TherapyEvent.Type.ANNOUNCEMENT -> PointsWithLabelGraphSeries.Shape.ANNOUNCEMENT
data.type == TherapyEvent.Type.APS_OFFLINE -> PointsWithLabelGraphSeries.Shape.OPENAPSOFFLINE
data.type == TherapyEvent.Type.EXERCISE -> PointsWithLabelGraphSeries.Shape.EXERCISE
duration > 0 -> PointsWithLabelGraphSeries.Shape.GENERALWITHDURATION
else -> PointsWithLabelGraphSeries.Shape.GENERAL
}
override fun getSize(): Float = if (rh.gb(R.bool.isTablet)) 12.0f else 10.0f
override fun getColor(): Int =
when (data.type) {
TherapyEvent.Type.ANNOUNCEMENT -> rh.gc(R.color.notificationAnnouncement)
TherapyEvent.Type.NS_MBG -> Color.RED
TherapyEvent.Type.FINGER_STICK_BG_VALUE -> Color.RED
TherapyEvent.Type.EXERCISE -> Color.BLUE
TherapyEvent.Type.APS_OFFLINE -> Color.GRAY and -0x7f000001
else -> Color.GRAY
}
override val size get() = if (rh.gb(R.bool.isTablet)) 12.0f else 10.0f
override val color
get() =
when (data.type) {
TherapyEvent.Type.ANNOUNCEMENT -> rh.gc(R.color.notificationAnnouncement)
TherapyEvent.Type.NS_MBG -> Color.RED
TherapyEvent.Type.FINGER_STICK_BG_VALUE -> Color.RED
TherapyEvent.Type.EXERCISE -> Color.BLUE
TherapyEvent.Type.APS_OFFLINE -> Color.GRAY and -0x7f000001
else -> Color.GRAY
}
}

View file

@ -664,13 +664,15 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
IobTotal bolusIob = iobCobCalculator.calculateIobFromBolus().round();
IobTotal basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round();
iobSum = DecimalFormatter.INSTANCE.to2Decimal(bolusIob.iob + basalIob.basaliob);
iobDetail = "(" + DecimalFormatter.INSTANCE.to2Decimal(bolusIob.iob) + "|" + DecimalFormatter.INSTANCE.to2Decimal(basalIob.basaliob) + ")";
iobSum = DecimalFormatter.INSTANCE.to2Decimal(bolusIob.getIob() + basalIob.getBasaliob());
iobDetail =
"(" + DecimalFormatter.INSTANCE.to2Decimal(bolusIob.getIob()) + "|" + DecimalFormatter.INSTANCE.to2Decimal(basalIob.getBasaliob()) + ")";
cobString = iobCobCalculator.getCobInfo(false, "WatcherUpdaterService").generateCOBString();
currentBasal = generateBasalString();
//bgi
double bgi = -(bolusIob.activity + basalIob.activity) * 5 * Profile.Companion.fromMgdlToUnits(profile.getIsfMgdl(), profileFunction.getUnits());
double bgi =
-(bolusIob.getActivity() + basalIob.getActivity()) * 5 * Profile.Companion.fromMgdlToUnits(profile.getIsfMgdl(), profileFunction.getUnits());
bgiString = "" + ((bgi >= 0) ? "+" : "") + DecimalFormatter.INSTANCE.to1Decimal(bgi);
status = generateStatusString(profile, currentBasal, iobSum, iobDetail, bgiString);

View file

@ -209,12 +209,12 @@ class IobCobOref1Thread internal constructor(
// figure out how many carbs that represents
// but always assume at least 3mg/dL/5m (default) absorption per active treatment
val ci = max(deviation, totalMinCarbsImpact)
if (ci != deviation) autosensData.failoverToMinAbsorbtionRate = true
if (ci != deviation) autosensData.failOverToMinAbsorptionRate = true
autosensData.absorbed = ci * profile.getIc(bgTime) / sens
// and add that to the running total carbsAbsorbed
autosensData.cob = max(previous.cob - autosensData.absorbed, 0.0)
autosensData.mealCarbs = previous.mealCarbs
autosensData.substractAbosorbedCarbs()
autosensData.deductAbsorbedCarbs()
autosensData.usedMinCarbsImpact = totalMinCarbsImpact
autosensData.absorbing = previous.absorbing
autosensData.mealStartCounter = previous.mealStartCounter

View file

@ -10,7 +10,6 @@ import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
@ -218,11 +217,11 @@ class IobCobThread @Inject internal constructor(
// figure out how many carbs that represents
// but always assume at least 3mg/dL/5m (default) absorption per active treatment
val ci = max(deviation, totalMinCarbsImpact)
if (ci != deviation) autosensData.failoverToMinAbsorbtionRate = true
if (ci != deviation) autosensData.failOverToMinAbsorptionRate = true
autosensData.absorbed = ci * profile.getIc(bgTime) / sens
// and add that to the running total carbsAbsorbed
autosensData.cob = max(previous.cob - autosensData.absorbed, 0.0)
autosensData.substractAbosorbedCarbs()
autosensData.deductAbsorbedCarbs()
autosensData.usedMinCarbsImpact = totalMinCarbsImpact
}
val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()

View file

@ -65,7 +65,7 @@ class LocalProfileFragment : DaggerFragment() {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
localProfilePlugin.currentProfile()?.dia = SafeParse.stringToDouble(binding.dia.text.toString())
localProfilePlugin.currentProfile()?.dia = SafeParse.stringToDouble(binding.dia.text)
localProfilePlugin.currentProfile()?.name = binding.name.text.toString()
doEdit()
}

View file

@ -8,6 +8,7 @@ import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.CommandQueue
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBus
@ -18,7 +19,7 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
class QueueThread internal constructor(
private val queue: CommandQueueImplementation,
private val queue: CommandQueue,
context: Context,
private val aapsLogger: AAPSLogger,
private val rxBus: RxBus,

View file

@ -32,7 +32,7 @@ class CommandBolus(
}
override fun status(): String {
return (if (detailedBolusInfo.insulin > 0) "BOLUS " + rh.gs(R.string.formatinsulinunits, detailedBolusInfo.insulin) else "") +
if (detailedBolusInfo.carbs > 0) "CARBS " + rh.gs(R.string.format_carbs, detailedBolusInfo.carbs.toInt()) else ""
return (if (detailedBolusInfo.insulin > 0) rh.gs(R.string.bolus_u_min, detailedBolusInfo.insulin) else "") +
if (detailedBolusInfo.carbs > 0) rh.gs(R.string.carbs_g, detailedBolusInfo.carbs.toInt()) else ""
}
}

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.queue.Callback
@ -19,5 +20,5 @@ class CommandCancelExtendedBolus constructor(
callback?.result(r)?.run()
}
override fun status(): String = "CANCEL EXTENDEDBOLUS"
override fun status(): String = rh.gs(R.string.uel_cancel_extended_bolus)
}

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.queue.Callback
@ -20,5 +21,5 @@ class CommandCancelTempBasal(
callback?.result(r)?.run()
}
override fun status(): String = "CANCEL TEMPBASAL"
override fun status(): String = rh.gs(R.string.uel_accepts_temp_basal)
}

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.queue.Callback
@ -21,5 +22,5 @@ class CommandExtendedBolus constructor(
callback?.result(r)?.run()
}
override fun status(): String = "EXTENDEDBOLUS $insulin U $durationInMinutes min"
override fun status(): String = rh.gs(R.string.extended_bolus_u_min, insulin, durationInMinutes)
}

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.androidaps.queue.Callback
@ -23,5 +24,5 @@ class CommandInsightSetTBROverNotification constructor(
}
@Suppress("SpellCheckingInspection")
override fun status(): String = "INSIGHTSETTBROVERNOTIFICATION"
override fun status(): String = rh.gs(R.string.insight_set_tbr_over_notification)
}

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Dana
import info.nightscout.androidaps.interfaces.Diaconn
@ -32,5 +33,5 @@ class CommandLoadEvents(
}
}
override fun status(): String = "LOAD EVENTS"
override fun status(): String = rh.gs(R.string.load_events)
}

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Dana
import info.nightscout.androidaps.interfaces.Diaconn
@ -33,5 +34,5 @@ class CommandLoadHistory(
}
}
override fun status(): String = "LOAD HISTORY $type"
override fun status(): String = rh.gs(R.string.load_history, type.toInt())
}

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.queue.Callback
@ -20,5 +21,5 @@ class CommandLoadTDDs(
callback?.result(r)?.run()
}
override fun status(): String = "LOAD TDDs"
override fun status(): String = rh.gs(R.string.load_tdds)
}

View file

@ -37,5 +37,5 @@ class CommandSMBBolus(
callback?.result(r)?.run()
}
override fun status(): String = "SMB BOLUS ${rh.gs(R.string.formatinsulinunits, detailedBolusInfo.insulin)}"
override fun status(): String = rh.gs(R.string.smb_bolus_u, detailedBolusInfo.insulin)
}

View file

@ -45,5 +45,5 @@ class CommandSetProfile constructor(
}
}
override fun status(): String = "SET PROFILE"
override fun status(): String = rh.gs(R.string.set_profile)
}

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Dana
import info.nightscout.androidaps.interfaces.Diaconn
@ -30,5 +31,5 @@ class CommandSetUserSettings(
}
}
override fun status(): String = "SET USER SETTINGS"
override fun status(): String = rh.gs(R.string.set_user_settings)
}

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.androidaps.queue.Callback
@ -21,5 +22,5 @@ class CommandStartPump(
}
}
override fun status(): String = "START PUMP"
override fun status(): String = rh.gs(R.string.start_pump)
}

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.androidaps.queue.Callback
@ -21,5 +22,5 @@ class CommandStopPump(
}
}
override fun status(): String = "STOP PUMP"
override fun status(): String = rh.gs(R.string.stop_pump)
}

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.PumpSync
@ -26,5 +27,5 @@ class CommandTempBasalAbsolute(
callback?.result(r)?.run()
}
override fun status(): String = "TEMP BASAL $absoluteRate U/h $durationInMinutes min"
override fun status(): String = rh.gs(R.string.temp_basal_absolute, absoluteRate, durationInMinutes)
}

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.PumpSync
@ -30,5 +31,5 @@ class CommandTempBasalPercent(
callback?.result(r)?.run()
}
override fun status(): String = "TEMP BASAL $percent% $durationInMinutes min"
override fun status(): String = rh.gs(R.string.temp_basal_percent, percent, durationInMinutes)
}

View file

@ -5,10 +5,7 @@ import android.util.AttributeSet
import android.view.LayoutInflater
import info.nightscout.androidaps.R
class NumberPickerVertical : NumberPicker {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
class NumberPickerVertical(context: Context, attrs: AttributeSet? = null) : NumberPicker(context, attrs) {
override fun inflate(context: Context) {
LayoutInflater.from(context).inflate(R.layout.number_picker_layout_vertical, this, true)

View file

@ -64,7 +64,7 @@
android:textStyle="bold" />
<info.nightscout.androidaps.utils.ui.NumberPicker
android:id="@+id/basalpercentinput"
android:id="@+id/basal_percent_input"
android:layout_width="130dp"
android:layout_height="40dp" />
@ -98,7 +98,7 @@
android:textStyle="bold" />
<info.nightscout.androidaps.utils.ui.NumberPicker
android:id="@+id/basalabsoluteinput"
android:id="@+id/basal_absolute_input"
android:layout_width="130dp"
android:layout_height="40dp" />

View file

@ -4,6 +4,7 @@ import android.content.Context
import android.os.PowerManager
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.androidaps.TestPumpPlugin
import info.nightscout.androidaps.database.AppRepository
@ -21,6 +22,7 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers
import org.mockito.Mock
import org.mockito.Mockito
@ -41,6 +43,7 @@ class QueueThreadTest : TestBaseWithProfile() {
}
if (it is CommandTempBasalAbsolute) {
it.activePlugin = activePlugin
it.rh = rh
}
}
}
@ -75,6 +78,7 @@ class QueueThreadTest : TestBaseWithProfile() {
val percentageConstraint = Constraint(0)
Mockito.`when`(constraintChecker.applyBasalPercentConstraints(anyObject(), anyObject()))
.thenReturn(percentageConstraint)
Mockito.`when`(rh.gs(ArgumentMatchers.eq(R.string.temp_basal_absolute), anyObject(), anyObject())).thenReturn("TEMP BASAL %1\$.2f U/h %2\$d min")
sut = QueueThread(commandQueue, context, aapsLogger, rxBus, activePlugin, rh, sp)
}

View file

@ -128,7 +128,7 @@ class EditEventDialog : DialogFragmentWithDate() {
// check for title
val title = binding.inputEventTitle.text?.toString() ?: return false
if (title.isEmpty()) {
ToastUtils.showToastInUiThread(context, R.string.automation_missing_task_name)
context?.let { ToastUtils.showToastInUiThread(it, R.string.automation_missing_task_name) }
return false
}
event.title = title
@ -137,12 +137,12 @@ class EditEventDialog : DialogFragmentWithDate() {
// check for at least one trigger
val con = event.trigger
if (con.size() == 0 && !event.userAction) {
ToastUtils.showToastInUiThread(context, R.string.automation_missing_trigger)
context?.let { ToastUtils.showToastInUiThread(it, R.string.automation_missing_trigger) }
return false
}
// check for at least one action
if (event.actions.isEmpty()) {
ToastUtils.showToastInUiThread(context, R.string.automation_missing_action)
context?.let { ToastUtils.showToastInUiThread(it, R.string.automation_missing_action) }
return false
}
// store

View file

@ -9,7 +9,7 @@ buildscript {
rxkotlin_version = '2.4.0'
room_version = '2.3.0'
lifecycle_version = '2.3.1'
dagger_version = '2.40.1'
dagger_version = '2.40.2'
coroutinesVersion = '1.4.1'
activityVersion = '1.3.1'
fragmentktx_version = '1.3.6'

View file

@ -10,18 +10,18 @@ import org.json.JSONObject
@Suppress("SpellCheckingInspection")
class IobTotal(val time: Long) : DataPointWithLabelInterface {
@JvmField var iob = 0.0
@JvmField var activity = 0.0
@JvmField var bolussnooze = 0.0
@JvmField var basaliob = 0.0
@JvmField var netbasalinsulin = 0.0
@JvmField var hightempinsulin = 0.0
var iob = 0.0
var activity = 0.0
var bolussnooze = 0.0
var basaliob = 0.0
var netbasalinsulin = 0.0
var hightempinsulin = 0.0
// oref1
@JvmField var lastBolusTime: Long = 0
var lastBolusTime: Long = 0
var iobWithZeroTemp: IobTotal? = null
@JvmField var netInsulin = 0.0 // for calculations from temp basals only
@JvmField var extendedBolusInsulin = 0.0 // total insulin for extended bolus
var netInsulin = 0.0 // for calculations from temp basals only
var extendedBolusInsulin = 0.0 // total insulin for extended bolus
fun copy(): IobTotal {
val i = IobTotal(time)
i.iob = iob
@ -104,36 +104,14 @@ class IobTotal(val time: Long) : DataPointWithLabelInterface {
}
// DataPoint interface
private var color = 0
override fun getX(): Double {
return time.toDouble()
}
override fun getY(): Double {
return iob
}
override var color = 0
override fun getX(): Double = time.toDouble()
override fun getY(): Double = iob
override fun setY(y: Double) {}
override fun getLabel(): String {
return ""
}
override fun getDuration(): Long {
return 0
}
override fun getShape(): PointsWithLabelGraphSeries.Shape {
return PointsWithLabelGraphSeries.Shape.IOBPREDICTION
}
override fun getSize(): Float {
return 0.5f
}
override fun getColor(): Int {
return color
}
override val label = ""
override val duration = 0L
override val shape = PointsWithLabelGraphSeries.Shape.IOBPREDICTION
override val size = 0.5f
fun setColor(color: Int): IobTotal {
this.color = color

View file

@ -164,7 +164,7 @@ class ProfileViewerDialog : DaggerDialogFragment() {
binding.date.text = date
binding.ic.text = it.getIcList(rh, dateUtil)
binding.isf.text = it.getIsfList(rh, dateUtil)
binding.basal.text = it.getBasalList(rh, dateUtil)
binding.basal.text = "" + rh.gs(R.string.formatinsulinunits, it.baseBasalSum()) + "\n" + it.getBasalList(rh, dateUtil)
binding.target.text = it.getTargetList(rh, dateUtil)
binding.basalGraph.show(it)

View file

@ -1,67 +0,0 @@
package info.nightscout.androidaps.plugins.aps.openAPSSMB;
/**
* Created by mike on 10.12.2017.
*/
public class SMBDefaults {
// CALCULATED OR FROM PREFS
// max_iob: 0 // if max_iob is not provided, will default to zero
// max_daily_safety_multiplier:3
// current_basal_safety_multiplier:4
// autosens_max:1.2
// autosens_min:0.7
// USED IN AUTOSENS
public final static boolean rewind_resets_autosens = true; // reset autosensitivity to neutral for awhile after each pump rewind
// USED IN TARGETS
// by default the higher end of the target range is used only for avoiding bolus wizard overcorrections
// use wide_bg_target_range: true to force neutral temps over a wider range of eventualBGs
public final static boolean wide_bg_target_range = false; // by default use only the low end of the pump's BG target range as OpenAPS target
// USED IN AUTOTUNE
public final static double autotune_isf_adjustmentFraction = 1.0; // keep autotune ISF closer to pump ISF via a weighted average of fullNewISF and pumpISF. 1.0 allows full adjustment, 0 is no adjustment from pump ISF.
public final static double remainingCarbsFraction = 1.0; // fraction of carbs we'll assume will absorb over 4h if we don't yet see carb absorption
// USED IN DETERMINE_BASAL
public final static boolean low_temptarget_lowers_sensitivity = false; // lower sensitivity for temptargets <= 99.
public final static boolean high_temptarget_raises_sensitivity = false; // raise sensitivity for temptargets >= 111. synonym for exercise_mode
public final static boolean sensitivity_raises_target = true; // raise BG target when autosens detects sensitivity
public final static boolean resistance_lowers_target = false; // lower BG target when autosens detects resistance
public final static boolean adv_target_adjustments = false; // lower target automatically when BG and eventualBG are high
public final static boolean exercise_mode = false; // when true, > 105 mg/dL high temp target adjusts sensitivityRatio for exercise_mode. This majorly changes the behavior of high temp targets from before. synonmym for high_temptarget_raises_sensitivity
public final static int half_basal_exercise_target = 160; // when temptarget is 160 mg/dL *and* exercise_mode=true, run 50% basal at this level (120 = 75%; 140 = 60%)
// create maxCOB and default it to 120 because that's the most a typical body can absorb over 4 hours.
// (If someone enters more carbs or stacks more; OpenAPS will just truncate dosing based on 120.
// Essentially, this just limits AMA/SMB as a safety cap against excessive COB entry)
public final static int maxCOB = 120;
//public final static boolean skip_neutral_temps = true; // ***** default false in oref1 ***** if true, don't set neutral temps
// unsuspend_if_no_temp:false // if true, pump will un-suspend after a zero temp finishes
// bolussnooze_dia_divisor:2 // bolus snooze decays after 1/2 of DIA
public final static double min_5m_carbimpact = 8d; // mg/dL per 5m (8 mg/dL/5m corresponds to 24g/hr at a CSF of 4 mg/dL/g (x/5*60/4))
public final static int remainingCarbsCap = 90; // max carbs we'll assume will absorb over 4h if we don't yet see carb absorption
// WARNING: use SMB with caution: it can and will automatically bolus up to max_iob worth of extra insulin
// enableUAM:true // enable detection of unannounced meal carb absorption
public final static boolean A52_risk_enable = false;
//public final static boolean enableSMB_with_COB = true; // ***** default false in oref1 ***** enable supermicrobolus while COB is positive
//public final static boolean enableSMB_with_temptarget = true; // ***** default false in oref1 ***** enable supermicrobolus for eating soon temp targets
// *** WARNING *** DO NOT USE enableSMB_always or enableSMB_after_carbs with xDrip+, Libre, or similar
// xDrip+, LimiTTer, etc. do not properly filter out high-noise SGVs
// Using SMB overnight with such data sources risks causing a dangerous overdose of insulin
// if the CGM sensor reads falsely high and doesn't come down as actual BG does
// public final static boolean enableSMB_always = false; // always enable supermicrobolus (unless disabled by high temptarget)
// *** WARNING *** DO NOT USE enableSMB_always or enableSMB_after_carbs with xDrip+, Libre, or similar
//public final static boolean enableSMB_after_carbs = false; // enable supermicrobolus for 6h after carbs, even with 0 COB
//public final static boolean allowSMB_with_high_temptarget = false; // allow supermicrobolus (if otherwise enabled) even with high temp targets
public final static int SMBInterval = 3; // minimum interval between SMBs, in minutes. (limited between 1 and 10 min)
public final static int maxSMBBasalMinutes = 30; // maximum minutes of basal that can be delivered as a single SMB with uncovered COB
public final static int maxUAMSMBBasalMinutes = 30; // maximum minutes of basal that can be delivered as a single SMB when IOB exceeds COB
// curve:"rapid-acting" // Supported curves: "bilinear", "rapid-acting" (Novolog, Novorapid, Humalog, Apidra) and "ultra-rapid" (Fiasp)
// useCustomPeakTime:false // allows changing insulinPeakTime
// insulinPeakTime:75 // number of minutes after a bolus activity peaks. defaults to 55m for Fiasp if useCustomPeakTime: false
public final static int carbsReqThreshold = 1; // grams of carbsReq to trigger a pushover
// offline_hotspot:false // enabled an offline-only local wifi hotspot if no Internet available
public final static double bolus_increment = 0.1; // minimum bolus that can be delivered as an SMB
}

View file

@ -0,0 +1,69 @@
package info.nightscout.androidaps.plugins.aps.openAPSSMB
@Suppress("SpellCheckingInspection")
object SMBDefaults {
// CALCULATED OR FROM PREFS
// max_iob: 0 // if max_iob is not provided, will default to zero
// max_daily_safety_multiplier:3
// current_basal_safety_multiplier:4
// autosens_max:1.2
// autosens_min:0.7
// USED IN AUTOSENS
// const val rewind_resets_autosens = true // reset autosensitivity to neutral for awhile after each pump rewind
// USED IN TARGETS
// by default the higher end of the target range is used only for avoiding bolus wizard overcorrections
// use wide_bg_target_range: true to force neutral temps over a wider range of eventualBGs
// const val wide_bg_target_range = false // by default use only the low end of the pump's BG target range as OpenAPS target
// USED IN AUTOTUNE
// const val autotune_isf_adjustmentFraction = 1.0 // keep autotune ISF closer to pump ISF via a weighted average of fullNewISF and pumpISF. 1.0 allows full adjustment, 0 is no adjustment from pump ISF.
// const val remainingCarbsFraction = 1.0 // fraction of carbs we'll assume will absorb over 4h if we don't yet see carb absorption
// USED IN DETERMINE_BASAL
// const val low_temptarget_lowers_sensitivity = false // lower sensitivity for temptargets <= 99.
const val high_temptarget_raises_sensitivity = false // raise sensitivity for temptargets >= 111. synonym for exercise_mode
const val sensitivity_raises_target = true // raise BG target when autosens detects sensitivity
const val resistance_lowers_target = false // lower BG target when autosens detects resistance
const val adv_target_adjustments = false // lower target automatically when BG and eventualBG are high
const val exercise_mode = false // when true, > 105 mg/dL high temp target adjusts sensitivityRatio for exercise_mode. This majorly changes the behavior of high temp targets from before. synonym for high_temptarget_raises_sensitivity
const val half_basal_exercise_target = 160 // when temptarget is 160 mg/dL *and* exercise_mode=true, run 50% basal at this level (120 = 75%; 140 = 60%)
// create maxCOB and default it to 120 because that's the most a typical body can absorb over 4 hours.
// (If someone enters more carbs or stacks more; OpenAPS will just truncate dosing based on 120.
// Essentially, this just limits AMA/SMB as a safety cap against excessive COB entry)
const val maxCOB = 120
//public final static boolean skip_neutral_temps = true; // ***** default false in oref1 ***** if true, don't set neutral temps
// un-suspend_if_no_temp:false // if true, pump will un-suspend after a zero temp finishes
// bolussnooze_dia_divisor:2 // bolus snooze decays after 1/2 of DIA
const val min_5m_carbimpact = 8.0 // mg/dL per 5m (8 mg/dL/5m corresponds to 24g/hr at a CSF of 4 mg/dL/g (x/5*60/4))
const val remainingCarbsCap = 90 // max carbs we'll assume will absorb over 4h if we don't yet see carb absorption
// WARNING: use SMB with caution: it can and will automatically bolus up to max_iob worth of extra insulin
// enableUAM:true // enable detection of unannounced meal carb absorption
const val A52_risk_enable = false
//public final static boolean enableSMB_with_COB = true; // ***** default false in oref1 ***** enable supermicrobolus while COB is positive
//public final static boolean enableSMB_with_temptarget = true; // ***** default false in oref1 ***** enable supermicrobolus for eating soon temp targets
// *** WARNING *** DO NOT USE enableSMB_always or enableSMB_after_carbs with xDrip+, Libre, or similar
// xDrip+, LimiTTer, etc. do not properly filter out high-noise SGVs
// Using SMB overnight with such data sources risks causing a dangerous overdose of insulin
// if the CGM sensor reads falsely high and doesn't come down as actual BG does
// public final static boolean enableSMB_always = false; // always enable supermicrobolus (unless disabled by high temptarget)
// *** WARNING *** DO NOT USE enableSMB_always or enableSMB_after_carbs with xDrip+, Libre, or similar
//public final static boolean enableSMB_after_carbs = false; // enable supermicrobolus for 6h after carbs, even with 0 COB
//public final static boolean allowSMB_with_high_temptarget = false; // allow supermicrobolus (if otherwise enabled) even with high temp targets
const val SMBInterval = 3 // minimum interval between SMBs, in minutes. (limited between 1 and 10 min)
const val maxSMBBasalMinutes = 30 // maximum minutes of basal that can be delivered as a single SMB with uncovered COB
const val maxUAMSMBBasalMinutes = 30 // maximum minutes of basal that can be delivered as a single SMB when IOB exceeds COB
// curve:"rapid-acting" // Supported curves: "bilinear", "rapid-acting" (Novolog, Novorapid, Humalog, Apidra) and "ultra-rapid" (Fiasp)
// useCustomPeakTime:false // allows changing insulinPeakTime
// insulinPeakTime:75 // number of minutes after a bolus activity peaks. defaults to 55m for Fiasp if useCustomPeakTime: false
const val carbsReqThreshold = 1 // grams of carbsReq to trigger a pushover
// offline_hotspot:false // enabled an offline-only local wifi hotspot if no Internet available
// const val bolus_increment = 0.1 // minimum bolus that can be delivered as an SMB
}

View file

@ -1,58 +0,0 @@
package info.nightscout.androidaps.plugins.general.overview.graphExtensions;
/**
* GraphView
* Copyright (C) 2014 Jonas Gehring
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License,
* with the "Linking Exception", which can be found at the license.txt
* file in this program.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* with the "Linking Exception" along with this program; if not,
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
*/
/**
* Added by mike
*/
import com.jjoe64.graphview.series.DataPointInterface;
/**
* interface of data points. Implement this in order
* to use your class in {@link com.jjoe64.graphview.series.Series}.
*
* You can also use the default implementation {@link com.jjoe64.graphview.series.DataPoint} so
* you do not have to implement it for yourself.
*
* @author jjoe64
*/
public interface DataPointWithLabelInterface extends DataPointInterface{
/**
* @return the x value
*/
double getX();
/**
* @return the y value
*/
double getY();
void setY(double y);
/**
* @return the label value
*/
String getLabel();
long getDuration();
PointsWithLabelGraphSeries.Shape getShape();
float getSize();
int getColor();
}

View file

@ -0,0 +1,16 @@
package info.nightscout.androidaps.plugins.general.overview.graphExtensions
import com.jjoe64.graphview.series.DataPointInterface
interface DataPointWithLabelInterface : DataPointInterface {
override fun getX(): Double
override fun getY(): Double
fun setY(y: Double)
val label: String?
val duration: Long
val shape: PointsWithLabelGraphSeries.Shape?
val size: Float
val color: Int
}

View file

@ -347,7 +347,7 @@ class AutosensDataStore {
synchronized(dataLock) {
var i = autosensDataTable.size() - 1
while (i >= 0 && count < valuesToProcess) {
if (autosensDataTable.valueAt(i).failoverToMinAbsorbtionRate) sum++
if (autosensDataTable.valueAt(i).failOverToMinAbsorptionRate) sum++
count++
i--
}

View file

@ -1,203 +0,0 @@
package info.nightscout.androidaps.plugins.iob.iobCobCalculator.data;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.core.R;
import info.nightscout.androidaps.interfaces.Profile;
import info.nightscout.androidaps.database.entities.Carbs;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults;
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface;
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries;
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
public class AutosensData implements DataPointWithLabelInterface {
@Inject AAPSLogger aapsLogger;
@Inject SP sp;
@Inject ResourceHelper rh;
@Inject ProfileFunction profileFunction;
@Inject DateUtil dateUtil;
public AutosensData(HasAndroidInjector injector) {
injector.androidInjector().inject(this);
}
public void setChartTime(long chartTime) {
this.chartTime = chartTime;
}
public class CarbsInPast {
long time;
double carbs;
public double min5minCarbImpact;
double remaining;
public CarbsInPast(Carbs t, boolean isAAPSOrWeighted) {
time = t.getTimestamp();
carbs = t.getAmount();
remaining = t.getAmount();
Profile profile = profileFunction.getProfile(t.getTimestamp());
if (isAAPSOrWeighted && profile != null) {
double maxAbsorptionHours = sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME);
double sens = profile.getIsfMgdl(t.getTimestamp());
double ic = profile.getIc(t.getTimestamp());
min5minCarbImpact = t.getAmount() / (maxAbsorptionHours * 60 / 5) * sens / ic;
aapsLogger.debug(LTag.AUTOSENS, "Min 5m carbs impact for " + carbs + "g @" + dateUtil.dateAndTimeString(t.getTimestamp()) + " for " + maxAbsorptionHours + "h calculated to " + min5minCarbImpact + " ISF: " + sens + " IC: " + ic);
} else {
min5minCarbImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact);
}
}
CarbsInPast(CarbsInPast other) {
this.time = other.time;
this.carbs = other.carbs;
this.min5minCarbImpact = other.min5minCarbImpact;
this.remaining = other.remaining;
}
@NonNull @Override
public String toString() {
return String.format(Locale.ENGLISH, "CarbsInPast: time: %s carbs: %.02f min5minCI: %.02f remaining: %.2f", dateUtil.dateAndTimeString(time), carbs, min5minCarbImpact, remaining);
}
}
public long time = 0L;
public double bg = 0; // mgdl
private long chartTime;
public String pastSensitivity = "";
public double deviation = 0d;
public boolean validDeviation = false;
public List<CarbsInPast> activeCarbsList = new ArrayList<>();
public double absorbed = 0d;
public double carbsFromBolus = 0d;
public double cob = 0;
public double bgi = 0d;
public double delta = 0d;
public double avgDelta = 0d;
public double avgDeviation = 0d;
public AutosensResult autosensResult = new AutosensResult();
public double slopeFromMaxDeviation = 0;
public double slopeFromMinDeviation = 999;
public double usedMinCarbsImpact = 0d;
public boolean failoverToMinAbsorbtionRate = false;
// Oref1
public boolean absorbing = false;
public double mealCarbs = 0;
public int mealStartCounter = 999;
public String type = "";
public boolean uam = false;
public List<Double> extraDeviation = new ArrayList<>();
@NonNull @Override
public String toString() {
return String.format(Locale.ENGLISH, "AutosensData: %s pastSensitivity=%s delta=%.02f avgDelta=%.02f bgi=%.02f deviation=%.02f avgDeviation=%.02f absorbed=%.02f carbsFromBolus=%.02f cob=%.02f autosensRatio=%.02f slopeFromMaxDeviation=%.02f slopeFromMinDeviation=%.02f activeCarbsList=%s",
dateUtil.dateAndTimeString(time), pastSensitivity, delta, avgDelta, bgi, deviation, avgDeviation, absorbed, carbsFromBolus, cob, autosensResult.getRatio(), slopeFromMaxDeviation, slopeFromMinDeviation, activeCarbsList.toString());
}
public List<CarbsInPast> cloneCarbsList() {
List<CarbsInPast> newActiveCarbsList = new ArrayList<>();
for (CarbsInPast c : activeCarbsList) {
newActiveCarbsList.add(new CarbsInPast(c));
}
return newActiveCarbsList;
}
// remove carbs older than timeframe
public void removeOldCarbs(long toTime, boolean isAAPSOrWeighted) {
double maxAbsorptionHours;
if (isAAPSOrWeighted) {
maxAbsorptionHours = sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME);
} else {
maxAbsorptionHours = sp.getDouble(R.string.key_absorption_cutoff, Constants.DEFAULT_MAX_ABSORPTION_TIME);
}
for (int i = 0; i < activeCarbsList.size(); i++) {
CarbsInPast c = activeCarbsList.get(i);
if (c.time + maxAbsorptionHours * 60 * 60 * 1000L < toTime) {
activeCarbsList.remove(i--);
if (c.remaining > 0)
cob -= c.remaining;
aapsLogger.debug(LTag.AUTOSENS, "Removing carbs at " + dateUtil.dateAndTimeString(toTime) + " after " + maxAbsorptionHours + "h > " + c.toString());
}
}
}
public void substractAbosorbedCarbs() {
double ac = absorbed;
for (int i = 0; i < activeCarbsList.size() && ac > 0; i++) {
CarbsInPast c = activeCarbsList.get(i);
if (c.remaining > 0) {
double sub = Math.min(ac, c.remaining);
c.remaining -= sub;
ac -= sub;
}
}
}
// ------- DataPointWithLabelInterface ------
private Scale scale;
public void setScale(Scale scale) {
this.scale = scale;
}
@Override
public double getX() {
return chartTime;
}
@Override
public double getY() {
return scale.transform(cob);
}
@Override
public void setY(double y) {
}
@Override
public String getLabel() {
return null;
}
@Override
public long getDuration() {
return 0;
}
@Override
public PointsWithLabelGraphSeries.Shape getShape() {
return PointsWithLabelGraphSeries.Shape.COBFAILOVER;
}
@Override
public float getSize() {
return 0.5f;
}
@Override
public int getColor() {
return rh.gc(R.color.cob);
}
}

View file

@ -0,0 +1,170 @@
package info.nightscout.androidaps.plugins.iob.iobCobCalculator.data
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.database.entities.Carbs
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.util.*
import javax.inject.Inject
import kotlin.math.min
class AutosensData(injector: HasAndroidInjector) : DataPointWithLabelInterface {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var sp: SP
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var dateUtil: DateUtil
inner class CarbsInPast {
var time: Long
var carbs: Double
var min5minCarbImpact = 0.0
var remaining: Double
constructor(t: Carbs, isAAPSOrWeighted: Boolean) {
time = t.timestamp
carbs = t.amount
remaining = t.amount
val profile = profileFunction.getProfile(t.timestamp)
if (isAAPSOrWeighted && profile != null) {
val maxAbsorptionHours = sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME)
val sens = profile.getIsfMgdl(t.timestamp)
val ic = profile.getIc(t.timestamp)
min5minCarbImpact = t.amount / (maxAbsorptionHours * 60 / 5) * sens / ic
aapsLogger.debug(
LTag.AUTOSENS,
"""Min 5m carbs impact for ${carbs}g @${dateUtil.dateAndTimeString(t.timestamp)} for ${maxAbsorptionHours}h calculated to $min5minCarbImpact ISF: $sens IC: $ic"""
)
} else {
min5minCarbImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact)
}
}
internal constructor(other: CarbsInPast) {
time = other.time
carbs = other.carbs
min5minCarbImpact = other.min5minCarbImpact
remaining = other.remaining
}
override fun toString(): String =
String.format(Locale.ENGLISH, "CarbsInPast: time: %s carbs: %.02f min5minCI: %.02f remaining: %.2f", dateUtil.dateAndTimeString(time), carbs, min5minCarbImpact, remaining)
}
var time = 0L
var bg = 0.0 // mgdl
var chartTime: Long = 0
var pastSensitivity = ""
var deviation = 0.0
var validDeviation = false
var activeCarbsList: MutableList<CarbsInPast> = ArrayList()
var absorbed = 0.0
var carbsFromBolus = 0.0
var cob = 0.0
var bgi = 0.0
var delta = 0.0
var avgDelta = 0.0
var avgDeviation = 0.0
var autosensResult = AutosensResult()
var slopeFromMaxDeviation = 0.0
var slopeFromMinDeviation = 999.0
var usedMinCarbsImpact = 0.0
var failOverToMinAbsorptionRate = false
// Oref1
var absorbing = false
var mealCarbs = 0.0
var mealStartCounter = 999
var type = ""
var uam = false
var extraDeviation: MutableList<Double> = ArrayList()
override fun toString(): String {
return String.format(
Locale.ENGLISH,
"AutosensData: %s pastSensitivity=%s delta=%.02f avgDelta=%.02f bgi=%.02f deviation=%.02f avgDeviation=%.02f absorbed=%.02f carbsFromBolus=%.02f cob=%.02f autosensRatio=%.02f slopeFromMaxDeviation=%.02f slopeFromMinDeviation=%.02f activeCarbsList=%s",
dateUtil.dateAndTimeString(time),
pastSensitivity,
delta,
avgDelta,
bgi,
deviation,
avgDeviation,
absorbed,
carbsFromBolus,
cob,
autosensResult.ratio,
slopeFromMaxDeviation,
slopeFromMinDeviation,
activeCarbsList.toString()
)
}
fun cloneCarbsList(): MutableList<CarbsInPast> {
val newActiveCarbsList: MutableList<CarbsInPast> = ArrayList()
for (c in activeCarbsList) {
newActiveCarbsList.add(CarbsInPast(c))
}
return newActiveCarbsList
}
// remove carbs older than timeframe
fun removeOldCarbs(toTime: Long, isAAPSOrWeighted: Boolean) {
val maxAbsorptionHours: Double =
if (isAAPSOrWeighted) sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME)
else sp.getDouble(R.string.key_absorption_cutoff, Constants.DEFAULT_MAX_ABSORPTION_TIME)
var i = 0
while (i < activeCarbsList.size) {
val c = activeCarbsList[i]
if (c.time + maxAbsorptionHours * 60 * 60 * 1000L < toTime) {
activeCarbsList.removeAt(i--)
if (c.remaining > 0) cob -= c.remaining
aapsLogger.debug(LTag.AUTOSENS, "Removing carbs at " + dateUtil.dateAndTimeString(toTime) + " after " + maxAbsorptionHours + "h > " + c.toString())
}
i++
}
}
fun deductAbsorbedCarbs() {
var ac = absorbed
var i = 0
while (i < activeCarbsList.size && ac > 0) {
val c = activeCarbsList[i]
if (c.remaining > 0) {
val sub = min(ac, c.remaining)
c.remaining -= sub
ac -= sub
}
i++
}
}
// ------- DataPointWithLabelInterface ------
var scale: Scale? = null
override fun getX(): Double = chartTime.toDouble()
override fun getY(): Double = scale!!.transform(cob)
override fun setY(y: Double) {}
override val label: String? = null
override val duration = 0L
override val shape = PointsWithLabelGraphSeries.Shape.COBFAILOVER
override val size = 0.5f
override val color get() = rh.gc(R.color.cob)
init {
injector.androidInjector().inject(this)
}
}

View file

@ -1,108 +0,0 @@
package info.nightscout.androidaps.utils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.media.MediaPlayer;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.DrawableRes;
import androidx.appcompat.view.ContextThemeWrapper;
import info.nightscout.androidaps.core.R;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
public class ToastUtils {
private static Toast lastToast = null;
public static class Long {
public static void warnToast(final Context ctx, final String string) {
graphicalToast(ctx, string, R.drawable.ic_toast_warn, false);
}
public static void infoToast(final Context ctx, final String string) {
graphicalToast(ctx, string, R.drawable.ic_toast_info, false);
}
public static void okToast(final Context ctx, final String string) {
graphicalToast(ctx, string, R.drawable.ic_toast_check, false);
}
public static void errorToast(final Context ctx, final String string) {
graphicalToast(ctx, string, R.drawable.ic_toast_error, false);
}
}
public static void showToastInUiThread(final Context ctx, final int stringId) {
showToastInUiThread(ctx, ctx.getString(stringId));
}
public static void warnToast(final Context ctx, final String string) {
graphicalToast(ctx, string, R.drawable.ic_toast_warn, true);
}
public static void infoToast(final Context ctx, final String string) {
graphicalToast(ctx, string, R.drawable.ic_toast_info, true);
}
public static void okToast(final Context ctx, final String string) {
graphicalToast(ctx, string, R.drawable.ic_toast_check, true);
}
public static void errorToast(final Context ctx, final String string) {
graphicalToast(ctx, string, R.drawable.ic_toast_error, true);
}
public static void graphicalToast(final Context ctx, final String string, @DrawableRes int iconId) {
graphicalToast(ctx, string, iconId, true);
}
@SuppressLint("InflateParams")
public static void graphicalToast(final Context ctx, final String string, @DrawableRes int iconId, boolean isShort) {
Handler mainThread = new Handler(Looper.getMainLooper());
mainThread.post(() -> {
View toastRoot = LayoutInflater.from(new ContextThemeWrapper(ctx, R.style.AppTheme)).inflate(R.layout.toast, null);
TextView toastMessage = toastRoot.findViewById(android.R.id.message);
toastMessage.setText(string);
ImageView toastIcon = toastRoot.findViewById(android.R.id.icon);
toastIcon.setImageResource(iconId);
if (lastToast != null) lastToast.cancel();
lastToast = new Toast(ctx);
lastToast.setDuration(isShort ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG);
lastToast.setView(toastRoot);
lastToast.show();
});
}
public static void showToastInUiThread(final Context ctx, final String string) {
Handler mainThread = new Handler(Looper.getMainLooper());
mainThread.post(() -> Toast.makeText(ctx, string, Toast.LENGTH_SHORT).show());
}
public static void showToastInUiThread(final Context ctx, final RxBus rxBus,
final String string, int soundID) {
showToastInUiThread(ctx, string);
playSound(ctx, soundID);
Notification notification = new Notification(Notification.TOAST_ALARM, string, Notification.URGENT);
rxBus.send(new EventNewNotification(notification));
}
private static void playSound(final Context ctx, final int soundID) {
final MediaPlayer soundMP = MediaPlayer.create(ctx, soundID);
soundMP.start();
soundMP.setOnCompletionListener(MediaPlayer::release);
}
}

View file

@ -0,0 +1,102 @@
package info.nightscout.androidaps.utils
import android.annotation.SuppressLint
import android.content.Context
import android.media.MediaPlayer
import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.DrawableRes
import androidx.appcompat.view.ContextThemeWrapper
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
object ToastUtils {
private var lastToast: Toast? = null
fun showToastInUiThread(ctx: Context, stringId: Int) {
showToastInUiThread(ctx, ctx.getString(stringId))
}
fun warnToast(ctx: Context?, string: String?) {
graphicalToast(ctx, string, R.drawable.ic_toast_warn, true)
}
fun infoToast(ctx: Context?, string: String?) {
graphicalToast(ctx, string, R.drawable.ic_toast_info, true)
}
fun okToast(ctx: Context?, string: String?) {
graphicalToast(ctx, string, R.drawable.ic_toast_check, true)
}
fun errorToast(ctx: Context?, string: String?) {
graphicalToast(ctx, string, R.drawable.ic_toast_error, true)
}
fun graphicalToast(ctx: Context?, string: String?, @DrawableRes iconId: Int) {
graphicalToast(ctx, string, iconId, true)
}
@SuppressLint("InflateParams")
fun graphicalToast(ctx: Context?, string: String?, @DrawableRes iconId: Int, isShort: Boolean) {
val mainThread = Handler(Looper.getMainLooper())
mainThread.post {
val toastRoot = LayoutInflater.from(ContextThemeWrapper(ctx, R.style.AppTheme)).inflate(R.layout.toast, null)
val toastMessage = toastRoot.findViewById<TextView>(android.R.id.message)
toastMessage.text = string
val toastIcon = toastRoot.findViewById<ImageView>(android.R.id.icon)
toastIcon.setImageResource(iconId)
lastToast?.cancel()
lastToast = Toast(ctx)
lastToast?.duration = if (isShort) Toast.LENGTH_SHORT else Toast.LENGTH_LONG
lastToast?.view = toastRoot
lastToast?.show()
}
}
fun showToastInUiThread(ctx: Context?, string: String?) {
val mainThread = Handler(Looper.getMainLooper())
mainThread.post { Toast.makeText(ctx, string, Toast.LENGTH_SHORT).show() }
}
fun showToastInUiThread(
ctx: Context?, rxBus: RxBus,
string: String?, soundID: Int
) {
showToastInUiThread(ctx, string)
playSound(ctx, soundID)
val notification = Notification(Notification.TOAST_ALARM, string!!, Notification.URGENT)
rxBus.send(EventNewNotification(notification))
}
private fun playSound(ctx: Context?, soundID: Int) {
val soundMP = MediaPlayer.create(ctx, soundID)
soundMP.start()
soundMP.setOnCompletionListener { obj: MediaPlayer -> obj.release() }
}
object Long {
fun warnToast(ctx: Context?, string: String) {
graphicalToast(ctx, string, R.drawable.ic_toast_warn, false)
}
fun infoToast(ctx: Context?, string: String) {
graphicalToast(ctx, string, R.drawable.ic_toast_info, false)
}
fun okToast(ctx: Context?, string: String) {
graphicalToast(ctx, string, R.drawable.ic_toast_check, false)
}
fun errorToast(ctx: Context?, string: String) {
graphicalToast(ctx, string, R.drawable.ic_toast_error, false)
}
}
}

View file

@ -13,16 +13,16 @@ class MinutesNumberPicker constructor(context: Context, attrs: AttributeSet? = n
}
override fun updateEditText() {
if (value == 0.0 && !allowZero) editText.setText("")
if (currentValue == 0.0 && !allowZero) editText?.setText("")
else {
if (focused) editText.setText(DecimalFormat("0").format(value))
if (focused) editText?.setText(DecimalFormat("0").format(currentValue))
else {
val hours = (value / 60).toInt()
val minutes = (value - hours * 60).toInt()
val hours = (currentValue / 60).toInt()
val minutes = (currentValue - hours * 60).toInt()
val formatted =
if (hours != 0) String.format(context.getString(R.string.format_hour_minute), hours, minutes)
else DecimalFormat("0").format(value)
editText.setText(formatted)
else DecimalFormat("0").format(currentValue)
editText?.setText(formatted)
}
}
}

View file

@ -1,343 +0,0 @@
package info.nightscout.androidaps.utils.ui;
import android.app.Service;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import java.text.NumberFormat;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import info.nightscout.androidaps.core.R;
import info.nightscout.androidaps.utils.SafeParse;
import info.nightscout.androidaps.utils.ToastUtils;
public class NumberPicker extends LinearLayout implements View.OnKeyListener,
View.OnTouchListener, View.OnClickListener {
public interface OnValueChangedListener {
void onValueChanged(double value);
}
EditText editText;
Button minusButton;
Button plusButton;
double value = 0;
double minValue = 0d;
double maxValue = 1d;
double step = 1d;
NumberFormat formatter;
boolean allowZero = false;
TextWatcher textWatcher = null;
Button okButton = null;
protected Boolean focused = false;
private Handler mHandler;
private ScheduledExecutorService mUpdater;
private OnValueChangedListener mOnValueChangedListener;
private class UpdateCounterTask implements Runnable {
private final boolean mInc;
private int repeated = 0;
private int multiplier = 1;
private final int doubleLimit = 5;
UpdateCounterTask(boolean inc) {
mInc = inc;
}
public void run() {
Message msg = new Message();
if (repeated % doubleLimit == 0) multiplier *= 2;
repeated++;
msg.arg1 = multiplier;
msg.arg2 = repeated;
if (mInc) {
msg.what = MSG_INC;
} else {
msg.what = MSG_DEC;
}
mHandler.sendMessage(msg);
}
}
private static final int MSG_INC = 0;
private static final int MSG_DEC = 1;
public NumberPicker(Context context) {
super(context, null);
this.initialize(context);
}
public NumberPicker(Context context, AttributeSet attrs) {
super(context, attrs);
this.initialize(context);
}
protected void inflate(Context context) {
LayoutInflater.from(context).inflate(R.layout.number_picker_layout, this, true);
}
protected void initialize(Context context) {
// set layout view
inflate(context);
// init ui components
minusButton = findViewById(R.id.decrement);
minusButton.setId(View.generateViewId());
plusButton = findViewById(R.id.increment);
plusButton.setId(View.generateViewId());
editText = findViewById(R.id.display);
editText.setId(View.generateViewId());
mHandler = new Handler(msg -> {
switch (msg.what) {
case MSG_INC:
inc(msg.arg1);
return true;
case MSG_DEC:
dec(msg.arg1);
return true;
}
return false;
});
minusButton.setOnTouchListener(this);
minusButton.setOnKeyListener(this);
minusButton.setOnClickListener(this);
plusButton.setOnTouchListener(this);
plusButton.setOnKeyListener(this);
plusButton.setOnClickListener(this);
setTextWatcher(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (focused) value = SafeParse.stringToDouble(editText.getText().toString());
callValueChangedListener();
if (okButton != null) {
if (value > maxValue || value < minValue)
okButton.setVisibility(INVISIBLE);
else
okButton.setVisibility(VISIBLE);
}
}
});
editText.setOnFocusChangeListener((v, hasFocus) -> {
focused = hasFocus;
if (!focused) getValue(); // check min/max
updateEditText();
});
}
@Override
public void setTag(Object tag) {
editText.setTag(tag);
}
public void setOnValueChangedListener(OnValueChangedListener onValueChangedListener) {
mOnValueChangedListener = onValueChangedListener;
}
public void setTextWatcher(TextWatcher textWatcher) {
this.textWatcher = textWatcher;
editText.addTextChangedListener(textWatcher);
editText.setOnFocusChangeListener((v, hasFocus) -> {
if (!hasFocus) {
value = SafeParse.stringToDouble(editText.getText().toString());
if (value > maxValue) {
value = maxValue;
ToastUtils.showToastInUiThread(getContext(), getContext().getString(R.string.youareonallowedlimit));
updateEditText();
if (okButton != null)
okButton.setVisibility(VISIBLE);
}
if (value < minValue) {
value = minValue;
ToastUtils.showToastInUiThread(getContext(), getContext().getString(R.string.youareonallowedlimit));
updateEditText();
if (okButton != null)
okButton.setVisibility(VISIBLE);
}
}
});
}
public void setParams(Double initValue, Double minValue, Double maxValue, Double step, NumberFormat formatter, boolean allowZero, Button okButton, TextWatcher textWatcher) {
if (this.textWatcher != null) {
editText.removeTextChangedListener(this.textWatcher);
}
setParams(initValue, minValue, maxValue, step, formatter, allowZero, okButton);
this.textWatcher = textWatcher;
if (textWatcher != null)
editText.addTextChangedListener(textWatcher);
}
public void setParams(Double initValue, Double minValue, Double maxValue, Double step, NumberFormat formatter, boolean allowZero, Button okButton) {
this.value = initValue;
this.minValue = minValue;
this.maxValue = maxValue;
this.step = step;
this.formatter = formatter;
this.allowZero = allowZero;
callValueChangedListener();
this.okButton = okButton;
editText.setKeyListener(DigitsKeyListenerWithComma.getInstance(minValue < 0, step != Math.rint(step)));
if (textWatcher != null)
editText.removeTextChangedListener(textWatcher);
updateEditText();
if (textWatcher != null)
editText.addTextChangedListener(textWatcher);
}
public void setValue(Double value) {
if (textWatcher != null)
editText.removeTextChangedListener(textWatcher);
this.value = value;
callValueChangedListener();
updateEditText();
if (textWatcher != null)
editText.addTextChangedListener(textWatcher);
}
public Double getValue() {
if (value > maxValue) {
value = maxValue;
ToastUtils.showToastInUiThread(getContext(), getContext().getString(R.string.youareonallowedlimit));
}
if (value < minValue) {
value = minValue;
ToastUtils.showToastInUiThread(getContext(), getContext().getString(R.string.youareonallowedlimit));
}
return value;
}
public String getText() {
return editText.getText().toString();
}
public void setStep(Double step) {
this.step = step;
}
private void inc(int multiplier) {
value += step * multiplier;
if (value > maxValue) {
value = maxValue;
callValueChangedListener();
ToastUtils.showToastInUiThread(getContext(), getContext().getString(R.string.youareonallowedlimit));
stopUpdating();
}
updateEditText();
}
private void dec(int multiplier) {
value -= step * multiplier;
if (value < minValue) {
value = minValue;
callValueChangedListener();
ToastUtils.showToastInUiThread(getContext(), getContext().getString(R.string.youareonallowedlimit));
stopUpdating();
}
updateEditText();
}
protected void updateEditText() {
if (value == 0d && !allowZero)
editText.setText("");
else
editText.setText(formatter.format(value));
}
private void callValueChangedListener() {
if (mOnValueChangedListener != null)
mOnValueChangedListener.onValueChanged(value);
}
private void startUpdating(boolean inc) {
if (mUpdater != null) {
//log.debug("Another executor is still active");
return;
}
mUpdater = Executors.newSingleThreadScheduledExecutor();
mUpdater.scheduleAtFixedRate(new UpdateCounterTask(inc), 200, 200,
TimeUnit.MILLISECONDS);
}
private void stopUpdating() {
if (mUpdater != null) {
mUpdater.shutdownNow();
mUpdater = null;
}
}
@Override
public void onClick(View v) {
if (mUpdater == null) {
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Service.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
editText.clearFocus();
if (v == plusButton) {
inc(1);
} else {
dec(1);
}
}
}
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
boolean isKeyOfInterest = keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER;
boolean isReleased = event.getAction() == KeyEvent.ACTION_UP;
boolean isPressed = event.getAction() == KeyEvent.ACTION_DOWN
&& event.getAction() != KeyEvent.ACTION_MULTIPLE;
if (isKeyOfInterest && isReleased) {
stopUpdating();
} else if (isKeyOfInterest && isPressed) {
startUpdating(v == plusButton);
}
return false;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
boolean isReleased = event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL;
boolean isPressed = event.getAction() == MotionEvent.ACTION_DOWN;
if (isReleased) {
stopUpdating();
} else if (isPressed) {
startUpdating(v == plusButton);
}
return false;
}
}

View file

@ -0,0 +1,295 @@
package info.nightscout.androidaps.utils.ui
import android.annotation.SuppressLint
import android.app.Service
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.text.Editable
import android.text.TextWatcher
import android.util.AttributeSet
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.View.OnFocusChangeListener
import android.view.View.OnTouchListener
import android.view.inputmethod.InputMethodManager
import android.widget.Button
import android.widget.EditText
import android.widget.LinearLayout
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.ToastUtils
import java.text.NumberFormat
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
import kotlin.math.round
@SuppressLint("ClickableViewAccessibility")
open class NumberPicker(context: Context, attrs: AttributeSet? = null) : LinearLayout(context, attrs), View.OnKeyListener, OnTouchListener, View.OnClickListener {
fun interface OnValueChangedListener {
fun onValueChanged(value: Double)
}
var editText: EditText? = null
private var minusButton: Button? = null
private var plusButton: Button? = null
var currentValue = 0.0
var minValue = 0.0
var maxValue = 1.0
var step = 1.0
var formatter: NumberFormat? = null
var allowZero = false
private var watcher: TextWatcher? = null
var okButton: Button? = null
protected var focused = false
private var mUpdater: ScheduledExecutorService? = null
private var mOnValueChangedListener: OnValueChangedListener? = null
private var mHandler: Handler = Handler(Looper.getMainLooper(), Handler.Callback { msg: Message ->
when (msg.what) {
MSG_INC -> {
inc(msg.arg1)
return@Callback true
}
MSG_DEC -> {
dec(msg.arg1)
return@Callback true
}
}
false
})
private inner class UpdateCounterTask(private val mInc: Boolean) : Runnable {
private var repeated = 0
private var multiplier = 1
private val doubleLimit = 5
override fun run() {
val msg = Message()
if (repeated % doubleLimit == 0) multiplier *= 2
repeated++
msg.arg1 = multiplier
msg.arg2 = repeated
if (mInc) {
msg.what = MSG_INC
} else {
msg.what = MSG_DEC
}
mHandler.sendMessage(msg)
}
}
protected open fun inflate(context: Context) {
LayoutInflater.from(context).inflate(R.layout.number_picker_layout, this, true)
}
protected fun initialize(context: Context) {
// set layout view
inflate(context)
// init ui components
minusButton = findViewById(R.id.decrement)
minusButton?.id = generateViewId()
plusButton = findViewById(R.id.increment)
plusButton?.id = generateViewId()
editText = findViewById(R.id.display)
editText?.id = generateViewId()
minusButton?.setOnTouchListener(this)
minusButton?.setOnKeyListener(this)
minusButton?.setOnClickListener(this)
plusButton?.setOnTouchListener(this)
plusButton?.setOnKeyListener(this)
plusButton?.setOnClickListener(this)
setTextWatcher(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable) {
if (focused) currentValue = SafeParse.stringToDouble(editText?.text.toString())
callValueChangedListener()
okButton?.visibility = if (currentValue > maxValue || currentValue < minValue) INVISIBLE else VISIBLE
}
})
editText?.setOnFocusChangeListener { _: View?, hasFocus: Boolean ->
focused = hasFocus
if (!focused) value // check min/max
updateEditText()
}
}
override fun setTag(tag: Any) {
editText?.tag = tag
}
fun setOnValueChangedListener(onValueChangedListener: OnValueChangedListener?) {
mOnValueChangedListener = onValueChangedListener
}
fun setTextWatcher(textWatcher: TextWatcher) {
watcher = textWatcher
editText?.addTextChangedListener(textWatcher)
editText?.onFocusChangeListener = OnFocusChangeListener { _: View?, hasFocus: Boolean ->
if (!hasFocus) {
currentValue = SafeParse.stringToDouble(editText?.text.toString())
if (currentValue > maxValue) {
currentValue = maxValue
ToastUtils.showToastInUiThread(context, context.getString(R.string.youareonallowedlimit))
updateEditText()
okButton?.visibility = VISIBLE
}
if (currentValue < minValue) {
currentValue = minValue
ToastUtils.showToastInUiThread(context, context.getString(R.string.youareonallowedlimit))
updateEditText()
okButton?.visibility = VISIBLE
}
}
}
}
fun setParams(initValue: Double, minValue: Double, maxValue: Double, step: Double, formatter: NumberFormat?, allowZero: Boolean, okButton: Button?, textWatcher: TextWatcher?) {
if (watcher != null) {
editText?.removeTextChangedListener(watcher)
}
setParams(initValue, minValue, maxValue, step, formatter, allowZero, okButton)
watcher = textWatcher
if (textWatcher != null) editText?.addTextChangedListener(textWatcher)
}
fun setParams(initValue: Double, minValue: Double, maxValue: Double, step: Double, formatter: NumberFormat?, allowZero: Boolean, okButton: Button?) {
currentValue = initValue
this.minValue = minValue
this.maxValue = maxValue
this.step = step
this.formatter = formatter
this.allowZero = allowZero
callValueChangedListener()
this.okButton = okButton
editText?.keyListener = DigitsKeyListenerWithComma.getInstance(minValue < 0, step != round(step))
if (watcher != null) editText?.removeTextChangedListener(watcher)
updateEditText()
if (watcher != null) editText?.addTextChangedListener(watcher)
}
var value: Double
get() {
if (currentValue > maxValue) {
currentValue = maxValue
ToastUtils.showToastInUiThread(context, context.getString(R.string.youareonallowedlimit))
}
if (currentValue < minValue) {
currentValue = minValue
ToastUtils.showToastInUiThread(context, context.getString(R.string.youareonallowedlimit))
}
return currentValue
}
set(value) {
if (watcher != null) editText?.removeTextChangedListener(watcher)
currentValue = value
callValueChangedListener()
updateEditText()
if (watcher != null) editText?.addTextChangedListener(watcher)
}
val text: String
get() = editText?.text.toString()
private fun inc(multiplier: Int) {
currentValue += step * multiplier
if (currentValue > maxValue) {
currentValue = maxValue
callValueChangedListener()
ToastUtils.showToastInUiThread(context, context.getString(R.string.youareonallowedlimit))
stopUpdating()
}
updateEditText()
}
private fun dec(multiplier: Int) {
currentValue -= step * multiplier
if (currentValue < minValue) {
currentValue = minValue
callValueChangedListener()
ToastUtils.showToastInUiThread(context, context.getString(R.string.youareonallowedlimit))
stopUpdating()
}
updateEditText()
}
protected open fun updateEditText() {
if (currentValue == 0.0 && !allowZero) editText?.setText("") else editText?.setText(formatter?.format(currentValue))
}
private fun callValueChangedListener() {
mOnValueChangedListener?.onValueChanged(currentValue)
}
private fun startUpdating(inc: Boolean) {
if (mUpdater != null) {
//log.debug("Another executor is still active");
return
}
mUpdater = Executors.newSingleThreadScheduledExecutor()
mUpdater?.scheduleAtFixedRate(
UpdateCounterTask(inc), 200, 200,
TimeUnit.MILLISECONDS
)
}
private fun stopUpdating() {
mUpdater?.shutdownNow()
mUpdater = null
}
override fun onClick(v: View) {
if (mUpdater == null) {
val imm = context.getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(editText?.windowToken, 0)
editText?.clearFocus()
if (v === plusButton) {
inc(1)
} else {
dec(1)
}
}
}
override fun onKey(v: View, keyCode: Int, event: KeyEvent): Boolean {
val isKeyOfInterest = keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER
val isReleased = event.action == KeyEvent.ACTION_UP
val isPressed = (event.action == KeyEvent.ACTION_DOWN)
if (isKeyOfInterest && isReleased) {
stopUpdating()
} else if (isKeyOfInterest && isPressed) {
startUpdating(v === plusButton)
}
return false
}
override fun onTouch(v: View, event: MotionEvent): Boolean {
val isReleased = event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL
val isPressed = event.action == MotionEvent.ACTION_DOWN
if (isReleased) {
stopUpdating()
} else if (isPressed) {
startUpdating(v === plusButton)
}
return false
}
companion object {
private const val MSG_INC = 0
private const val MSG_DEC = 1
}
init {
initialize(context)
}
}

View file

@ -504,7 +504,22 @@
<string name="basal_value">Basal value</string>
<string name="nsclient_version_does_not_match">NSClient version doesn\'t match to AndroidAPS. Please update.</string>
<!-- readStatus reasons -->
<!-- Command Queue + readStatus reasons -->
<string name="bolus_u_min">BOLUS %1$.2f U</string>
<string name="carbs_g">CARBS %1$d g</string>
<string name="extended_bolus_u_min">EXTENDED BOLUS %1$.2f U %2$d min</string>
<string name="load_events">LOAD EVENTS</string>
<string name="load_history">LOAD HISTORY %1$d</string>
<string name="load_tdds">LOAD TDDs</string>
<string name="set_profile">SET PROFILE</string>
<string name="set_user_settings">SET USER SETTINGS</string>
<string name="smb_bolus_u">SMB BOLUS %1$.2f U</string>
<string name="start_pump">START PUMP</string>
<string name="stop_pump">STOP PUMP</string>
<string name="temp_basal_absolute">TEMP BASAL %1$.2f U/h %2$d min</string>
<string name="temp_basal_percent">TEMP BASAL %1$d%% %2$d min</string>
<string name="insight_set_tbr_over_notification">INSIGHT SET TBR OVER NOTIFICATION</string>
<string name="read_status" comment="10 characters max for READSTATUS translation">READSTATUS %1$s</string>
<string name="keepalive_status_outdated" comment="26 characters max for translation">KeepAlive. Status outdated.</string>
<string name="keepalive_basal_outdated" comment="26 characters max for translation">KeepAlive. Basal outdated.</string>
<string name="sms" comment="26 characters max for translation">SMS</string>
@ -520,7 +535,6 @@
<string name="bolus_ok" comment="26 characters max for translation">Bolus OK</string>
<string name="pump_paired" comment="26 characters max for translation">Pump paired</string>
<string name="insight_refresh_button" comment="26 characters max for translation">Insight Refresh Button</string>
<string name="read_status" comment="10 characters max for READSTATUS translation">READSTATUS %1$s</string>
<plurals name="days">
<item quantity="one">%1$d day</item>

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.danaRKorean.comm
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.dana.DanaPump
import info.nightscout.androidaps.danar.R
import info.nightscout.androidaps.danar.comm.MessageBase
import info.nightscout.androidaps.events.EventRebuildTabs

View file

@ -207,10 +207,12 @@ public abstract class AbstractDanaRExecutionService extends DaggerService {
}
}
} else {
ToastUtils.showToastInUiThread(context.getApplicationContext(), rh.gs(R.string.nobtadapter));
ToastUtils.INSTANCE.showToastInUiThread(context.getApplicationContext(),
rh.gs(R.string.nobtadapter));
}
if (mBTDevice == null) {
ToastUtils.showToastInUiThread(context.getApplicationContext(), rh.gs(R.string.devicenotfound));
ToastUtils.INSTANCE.showToastInUiThread(context.getApplicationContext(),
rh.gs(R.string.devicenotfound));
}
}

View file

@ -6,12 +6,17 @@ import info.nightscout.androidaps.dana.DanaPump
import info.nightscout.androidaps.danars.encryption.BleEncryption
import javax.inject.Inject
@Suppress("MemberVisibilityCanBePrivate")
class DanaRSPacketGeneralInitialScreenInformation(
injector: HasAndroidInjector
) : DanaRSPacket(injector) {
@Inject lateinit var danaPump: DanaPump
var isTempBasalInProgress = false
var isExtendedInProgress = false
var isDualBolusInProgress = false
init {
opCode = BleEncryption.DANAR_PACKET__OPCODE_REVIEW__INITIAL_SCREEN_INFORMATION
aapsLogger.debug(LTag.PUMPCOMM, "New message")
@ -22,42 +27,22 @@ class DanaRSPacketGeneralInitialScreenInformation(
failed = true
return
} else failed = false
var dataIndex = DATA_START
var dataSize = 1
val status = byteArrayToInt(getBytes(data, dataIndex, dataSize))
val status = intFromBuff(data, 0, 1)
danaPump.pumpSuspended = status and 0x01 == 0x01
val isTempBasalInProgress = status and 0x10 == 0x10
val isExtendedInProgress = status and 0x04 == 0x04
val isDualBolusInProgress = status and 0x08 == 0x08
dataIndex += dataSize
dataSize = 2
danaPump.dailyTotalUnits = byteArrayToInt(getBytes(data, dataIndex, dataSize)) / 100.0
dataIndex += dataSize
dataSize = 2
danaPump.maxDailyTotalUnits = (byteArrayToInt(getBytes(data, dataIndex, dataSize)) / 100.0).toInt()
dataIndex += dataSize
dataSize = 2
danaPump.reservoirRemainingUnits = byteArrayToInt(getBytes(data, dataIndex, dataSize)) / 100.0
dataIndex += dataSize
dataSize = 2
danaPump.currentBasal = byteArrayToInt(getBytes(data, dataIndex, dataSize)) / 100.0
dataIndex += dataSize
dataSize = 1
val tempBasalPercent = byteArrayToInt(getBytes(data, dataIndex, dataSize))
dataIndex += dataSize
dataSize = 1
danaPump.batteryRemaining = byteArrayToInt(getBytes(data, dataIndex, dataSize))
dataIndex += dataSize
dataSize = 2
val extendedBolusAbsoluteRate = byteArrayToInt(getBytes(data, dataIndex, dataSize)) / 100.0
dataIndex += dataSize
dataSize = 2
danaPump.iob = byteArrayToInt(getBytes(data, dataIndex, dataSize)) / 100.0
isTempBasalInProgress = status and 0x10 == 0x10
isExtendedInProgress = status and 0x04 == 0x04
isDualBolusInProgress = status and 0x08 == 0x08
danaPump.dailyTotalUnits = intFromBuff(data, 1, 2) / 100.0
danaPump.maxDailyTotalUnits = intFromBuff(data, 3, 2) / 100
danaPump.reservoirRemainingUnits = intFromBuff(data, 5, 2) / 100.0
danaPump.currentBasal = intFromBuff(data, 7, 2) / 100.0
val tempBasalPercent = intFromBuff(data, 9, 1)
danaPump.batteryRemaining = intFromBuff(data, 10, 1)
val extendedBolusAbsoluteRate = intFromBuff(data, 11, 2) / 100.0
danaPump.iob = intFromBuff(data, 13, 2) / 100.0
if (data.size >= 18) {
//protocol 10+
dataIndex += dataSize
dataSize = 1
danaPump.errorState = DanaPump.ErrorState[byteArrayToInt(getBytes(data, dataIndex, dataSize))]
danaPump.errorState = DanaPump.ErrorState[intFromBuff(data, 15, 1)]
?: DanaPump.ErrorState.NONE
aapsLogger.debug(LTag.PUMPCOMM, "ErrorState: " + danaPump.errorState.name)
}

View file

@ -338,7 +338,9 @@ class DanaRSService : DaggerService() {
fun tempBasal(percent: Int, durationInHours: Int): Boolean {
if (!isConnected) return false
if (danaPump.isTempBasalInProgress) {
val status = DanaRSPacketGeneralInitialScreenInformation(injector)
sendMessage(status)
if (status.isTempBasalInProgress) {
rxBus.send(EventPumpStatusChanged(rh.gs(R.string.stoppingtempbasal)))
sendMessage(DanaRSPacketBasalSetCancelTemporaryBasal(injector))
SystemClock.sleep(500)
@ -348,6 +350,7 @@ class DanaRSService : DaggerService() {
sendMessage(msgTBR)
SystemClock.sleep(200)
loadEvents()
SystemClock.sleep(4500)
val tbr = pumpSync.expectedPumpState().temporaryBasal
danaPump.fromTemporaryBasal(tbr)
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING))
@ -355,7 +358,9 @@ class DanaRSService : DaggerService() {
}
fun highTempBasal(percent: Int): Boolean {
if (danaPump.isTempBasalInProgress) {
val status = DanaRSPacketGeneralInitialScreenInformation(injector)
sendMessage(status)
if (status.isTempBasalInProgress) {
rxBus.send(EventPumpStatusChanged(rh.gs(R.string.stoppingtempbasal)))
sendMessage(DanaRSPacketBasalSetCancelTemporaryBasal(injector))
SystemClock.sleep(500)
@ -364,6 +369,7 @@ class DanaRSService : DaggerService() {
val msgTBR = DanaRSPacketAPSBasalSetTemporaryBasal(injector, percent)
sendMessage(msgTBR)
loadEvents()
SystemClock.sleep(4500)
val tbr = pumpSync.expectedPumpState().temporaryBasal
danaPump.fromTemporaryBasal(tbr)
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING))
@ -375,7 +381,9 @@ class DanaRSService : DaggerService() {
aapsLogger.error(LTag.PUMPCOMM, "Wrong duration param")
return false
}
if (danaPump.isTempBasalInProgress) {
val status = DanaRSPacketGeneralInitialScreenInformation(injector)
sendMessage(status)
if (status.isTempBasalInProgress) {
rxBus.send(EventPumpStatusChanged(rh.gs(R.string.stoppingtempbasal)))
sendMessage(DanaRSPacketBasalSetCancelTemporaryBasal(injector))
SystemClock.sleep(500)
@ -384,6 +392,7 @@ class DanaRSService : DaggerService() {
val msgTBR = DanaRSPacketAPSBasalSetTemporaryBasal(injector, percent)
sendMessage(msgTBR)
loadEvents()
SystemClock.sleep(4500)
val tbr = pumpSync.expectedPumpState().temporaryBasal
aapsLogger.debug(LTag.PUMPCOMM, "Expected TBR found: $tbr")
danaPump.fromTemporaryBasal(tbr)
@ -397,6 +406,7 @@ class DanaRSService : DaggerService() {
val msgCancel = DanaRSPacketBasalSetCancelTemporaryBasal(injector)
sendMessage(msgCancel)
loadEvents()
SystemClock.sleep(4500)
val tbr = pumpSync.expectedPumpState().temporaryBasal
danaPump.fromTemporaryBasal(tbr)
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING))
@ -410,6 +420,7 @@ class DanaRSService : DaggerService() {
sendMessage(msgExtended)
SystemClock.sleep(200)
loadEvents()
SystemClock.sleep(4500)
val eb = pumpSync.expectedPumpState().extendedBolus
danaPump.fromExtendedBolus(eb)
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING))
@ -422,6 +433,7 @@ class DanaRSService : DaggerService() {
val msgStop = DanaRSPacketBolusSetExtendedBolusCancel(injector)
sendMessage(msgStop)
loadEvents()
SystemClock.sleep(4500)
val eb = pumpSync.expectedPumpState().extendedBolus
danaPump.fromExtendedBolus(eb)
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING))

View file

@ -0,0 +1,9 @@
package info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command
import info.nightscout.androidaps.queue.commands.CustomCommand
class CommandDisableSuspendAlerts: CustomCommand {
override val statusDescription: String
get() = "DISABLE SUSPEND ALERTS"
}

View file

@ -502,7 +502,10 @@ class OmnipodDashPumpPlugin @Inject constructor(
// prevent setBasal requests
return true
}
// TODO: what do we have to answer here if delivery is suspended?
if (podStateManager.isSuspended) {
// set new basal profile failed midway
return false
}
val running = podStateManager.basalProgram
val equal = (mapProfileToBasalProgram(profile) == running)
aapsLogger.info(LTag.PUMP, "set: $equal. profile=$profile, running=$running")
@ -1089,8 +1092,6 @@ class OmnipodDashPumpPlugin @Inject constructor(
return when (customCommand) {
is CommandSilenceAlerts ->
silenceAlerts()
is CommandSuspendDelivery ->
suspendDelivery()
is CommandResumeDelivery ->
resumeDelivery()
is CommandDeactivatePod ->
@ -1101,7 +1102,8 @@ class OmnipodDashPumpPlugin @Inject constructor(
updateAlertConfiguration()
is CommandPlayTestBeep ->
playTestBeep()
is CommandDisableSuspendAlerts ->
disableSuspendAlerts()
else -> {
aapsLogger.warn(LTag.PUMP, "Unsupported custom command: " + customCommand.javaClass.name)
PumpEnactResult(injector).success(false).enacted(false).comment(
@ -1124,27 +1126,28 @@ class OmnipodDashPumpPlugin @Inject constructor(
} ?: PumpEnactResult(injector).success(false).enacted(false).comment("No active alerts") // TODO i18n
}
private fun suspendDelivery(): PumpEnactResult {
return executeProgrammingCommand(
historyEntry = history.createRecord(OmnipodCommandType.SUSPEND_DELIVERY),
command = omnipodManager.suspendDelivery(hasBasalBeepEnabled())
.filter { podEvent -> podEvent.isCommandSent() }
.map {
pumpSyncTempBasal(
0.0,
PodConstants.MAX_POD_LIFETIME.toMinutes(),
PumpSync.TemporaryBasalType.PUMP_SUSPEND
)
}
.ignoreElements(),
pre = observeDeliveryActive(),
).doFinally {
notifyOnUnconfirmed(
Notification.PUMP_ERROR,
"Unconfirmed suspendDelivery command. Please refresh pod status",
R.raw.boluserror
)
}.toPumpEnactResult()
private fun disableSuspendAlerts(): PumpEnactResult {
val alerts = listOf(
AlertConfiguration(
AlertType.SUSPEND_ENDED,
enabled = false,
durationInMinutes = 0,
autoOff = false,
AlertTrigger.TimerTrigger(
0
),
BeepType.FOUR_TIMES_BIP_BEEP,
BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN
),
)
val ret = executeProgrammingCommand(
historyEntry = history.createRecord(OmnipodCommandType.CONFIGURE_ALERTS),
command = omnipodManager.programAlerts(alerts).ignoreElements(),
).toPumpEnactResult()
if (ret.success && ret.enacted) {
podStateManager.suspendAlertsEnabled = false
}
return ret
}
private fun observeDeliveryActive(): Completable = Completable.defer {
@ -1249,7 +1252,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
expiryAlertDelay.toMinutes().toShort()
),
BeepType.FOUR_TIMES_BIP_BEEP,
BeepRepetitionType.XXX2
BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN
)
)
return executeProgrammingCommand(
@ -1369,9 +1372,11 @@ class OmnipodDashPumpPlugin @Inject constructor(
)
podStateManager.tempBasal = null
rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED))
rxBus.send(EventDismissNotification(Notification.FAILED_UPDATE_PROFILE))
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS))
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TIME_OUT_OF_SYNC))
commandQueue.customCommand(CommandDisableSuspendAlerts(), null)
}
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS))
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TIME_OUT_OF_SYNC))
}
OmnipodCommandType.SET_BASAL_PROFILE -> {
@ -1394,6 +1399,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
rxBus.send(EventDismissNotification(Notification.FAILED_UPDATE_PROFILE))
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS))
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TIME_OUT_OF_SYNC))
commandQueue.customCommand(CommandDisableSuspendAlerts(), null)
}
}

View file

@ -12,8 +12,10 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definitio
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.POD_EXPIRATION_ALERT_HOURS
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.POD_EXPIRATION_ALERT_HOURS_DURATION
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.POD_EXPIRATION_IMMINENT_ALERT_HOURS
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.POD_PULSE_BOLUS_UNITS
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.*
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.Observable
import io.reactivex.functions.Action
@ -281,7 +283,7 @@ class OmnipodDashManagerImpl @Inject constructor(
.setUniqueId(podStateManager.uniqueId!!.toInt())
.setSequenceNumber(podStateManager.messageSequenceNumber)
.setNonce(NONCE)
.setNumberOfUnits(podStateManager.firstPrimeBolusVolume!! * PodConstants.POD_PULSE_BOLUS_UNITS)
.setNumberOfUnits(Round.roundTo(podStateManager.firstPrimeBolusVolume!! * PodConstants.POD_PULSE_BOLUS_UNITS, POD_PULSE_BOLUS_UNITS))
.setDelayBetweenPulsesInEighthSeconds(podStateManager.primePulseRate!!.toByte())
.setProgramReminder(ProgramReminder(atStart = false, atEnd = false, atInterval = 0))
.build(),
@ -383,7 +385,7 @@ class OmnipodDashManagerImpl @Inject constructor(
)
observables.add(
observeSendProgramBolusCommand(
podStateManager.secondPrimeBolusVolume!! * PodConstants.POD_PULSE_BOLUS_UNITS,
Round.roundTo(podStateManager.secondPrimeBolusVolume!! * PodConstants.POD_PULSE_BOLUS_UNITS, PodConstants.POD_PULSE_BOLUS_UNITS),
podStateManager.primePulseRate!!.toByte(),
confirmationBeeps = false,
completionBeeps = false
@ -437,7 +439,7 @@ class OmnipodDashManagerImpl @Inject constructor(
userExpiryAlertDelay.toMinutes().toShort()
),
BeepType.FOUR_TIMES_BIP_BEEP,
BeepRepetitionType.XXX2
BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN
)
)
}
@ -502,8 +504,29 @@ class OmnipodDashManagerImpl @Inject constructor(
return Observable.concat(
observePodRunning,
observeConnectToPod,
observeSendStopDeliveryCommand(StopDeliveryCommand.DeliveryType.ALL, hasBasalBeepEnabled)
).interceptPodEvents()
observeSuspendDeliveryCommand(hasBasalBeepEnabled)
).doOnComplete {
podStateManager.suspendAlertsEnabled = true
}.interceptPodEvents()
}
private fun observeSuspendDeliveryCommand(hasBasalBeepEnabled: Boolean): Observable<PodEvent> {
return Observable.defer {
val beepType = if (!hasBasalBeepEnabled)
BeepType.SILENT
else
BeepType.LONG_SINGLE_BEEP
bleManager.sendCommand(
SuspendDeliveryCommand.Builder()
.setSequenceNumber(podStateManager.messageSequenceNumber)
.setUniqueId(podStateManager.uniqueId!!.toInt())
.setNonce(NONCE)
.setBeepType(beepType)
.build(),
DefaultStatusResponse::class
)
}
}
private fun observeSendProgramTempBasalCommand(rate: Double, durationInMinutes: Short, tempBasalBeeps: Boolean): Observable<PodEvent> {

View file

@ -38,6 +38,18 @@ class ProgramAlertsCommand private constructor(
return appendCrc(byteBuffer.array())
}
val encodedWithoutHeaderAndCRC32: ByteArray
get() {
val byteBuffer: ByteBuffer = ByteBuffer.allocate(getLength().toInt())
.put(commandType.value)
.put(getBodyLength())
.putInt(nonce)
for (configuration in alertConfigurations) {
byteBuffer.put(configuration.encoded)
}
return byteBuffer.array()
}
override fun toString(): String {
return "ProgramAlertsCommand{" +
"alertConfigurations=" + alertConfigurations +

View file

@ -0,0 +1,113 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.*
import java.nio.ByteBuffer
import java.util.*
// StopDelivery.ALL followed by ProgramAlerts
class SuspendDeliveryCommand private constructor(
uniqueId: Int,
sequenceNumber: Short,
multiCommandFlag: Boolean,
private val beepType: BeepType,
nonce: Int
) : NonceEnabledCommand(CommandType.STOP_DELIVERY, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
override val encoded: ByteArray
get() {
val alerts = listOf(
AlertConfiguration(
AlertType.SUSPEND_ENDED,
enabled = true,
durationInMinutes = 0,
autoOff = false,
AlertTrigger.TimerTrigger(
20
),
BeepType.FOUR_TIMES_BIP_BEEP,
BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN
),
)
val programAlerts = ProgramAlertsCommand.Builder()
.setNonce(nonce)
.setUniqueId(uniqueId)
.setAlertConfigurations(alerts)
.setSequenceNumber(sequenceNumber)
.setMultiCommandFlag(false)
.build()
.encodedWithoutHeaderAndCRC32
val byteBuffer = ByteBuffer.allocate(LENGTH + HEADER_LENGTH + programAlerts.size)
.put(encodeHeader(uniqueId, sequenceNumber, (LENGTH + programAlerts.size).toShort(), multiCommandFlag))
.put(commandType.value)
.put(BODY_LENGTH)
.putInt(nonce)
.put((beepType.value.toInt() shl 4 or DeliveryType.ALL.encoded[0].toInt()).toByte())
.put(programAlerts)
.array()
return appendCrc(
byteBuffer
)
}
override fun toString(): String {
return "SuspendDeliveryCommand{" +
"deliveryType=" + DeliveryType.ALL +
", beepType=" + beepType +
", nonce=" + nonce +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}'
}
enum class DeliveryType(
private val basal: Boolean,
private val tempBasal: Boolean,
private val bolus: Boolean
) : Encodable {
BASAL(true, false, false), TEMP_BASAL(false, true, false), BOLUS(false, false, true), ALL(true, true, true);
override val encoded: ByteArray
get() {
val bitSet = BitSet(8)
bitSet[0] = basal
bitSet[1] = tempBasal
bitSet[2] = bolus
return bitSet.toByteArray()
}
}
class Builder : NonceEnabledCommandBuilder<Builder, SuspendDeliveryCommand>() {
private var beepType: BeepType? = BeepType.LONG_SINGLE_BEEP
fun setBeepType(beepType: BeepType): Builder {
this.beepType = beepType
return this
}
override fun buildCommand(): SuspendDeliveryCommand {
requireNotNull(beepType) { "beepType can not be null" }
return SuspendDeliveryCommand(
uniqueId!!,
sequenceNumber!!,
multiCommandFlag,
beepType!!,
nonce!!
)
}
}
companion object {
private const val LENGTH: Short = 7
private const val BODY_LENGTH: Byte = 5
}
}

View file

@ -6,8 +6,8 @@ enum class BeepRepetitionType(
) {
XXX(0x01.toByte()), // Used in lump of coal alert, LOW_RESERVOIR
XXX2(0x03.toByte()), // Used in USER_SET_EXPIRATION
EVERY_MINUTE_AND_EVERY_15_MIN(0x03.toByte()), // Used in USER_SET_EXPIRATION, suspend delivery
XXX3(0x05.toByte()), // published system expiration alert
XXX4(0x06.toByte()), // Used in imminent pod expiration alert, suspend in progress
XXX4(0x06.toByte()), // Used in imminent pod expiration alert, suspend in progress. No repeat?
XXX5(0x08.toByte()); // Lump of coal alert
}

View file

@ -77,6 +77,7 @@ interface OmnipodDashPodStateManager {
var basalProgram: BasalProgram?
val activeCommand: ActiveCommand?
val lastBolus: LastBolus?
var suspendAlertsEnabled: Boolean
fun increaseMessageSequenceNumber()
fun increaseEapAkaSequenceNumber(): ByteArray

View file

@ -16,6 +16,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.DefaultStatusResponse
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.SetUniqueIdResponse
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.Completable
import io.reactivex.Maybe
@ -224,6 +225,13 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
store()
}
override var suspendAlertsEnabled: Boolean
get() = podState.suspendAlertsEnabled
set(enabled) {
podState.suspendAlertsEnabled = enabled
store()
}
override val lastStatusResponseReceived: Long
get() = podState.lastStatusResponseReceived
@ -340,7 +348,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
private fun updateLastBolusFromResponse(bolusPulsesRemaining: Short) {
podState.lastBolus?.run {
val remainingUnits = bolusPulsesRemaining.toDouble() * PodConstants.POD_PULSE_BOLUS_UNITS
val remainingUnits = Round.roundTo(bolusPulsesRemaining * PodConstants.POD_PULSE_BOLUS_UNITS, PodConstants.POD_PULSE_BOLUS_UNITS)
this.bolusUnitsRemaining = remainingUnits
if (remainingUnits == 0.0) {
this.deliveryComplete = true
@ -713,6 +721,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
var timeZoneOffset: Int? = null
var timeZoneUpdated: Long? = null
var alarmSynced: Boolean = false
var suspendAlertsEnabled: Boolean = false
var bleVersion: SoftwareVersion? = null
var firmwareVersion: SoftwareVersion? = null

View file

@ -439,6 +439,10 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
R.string.omnipod_common_alert_expiration_advisory
AlertType.AUTO_OFF ->
R.string.omnipod_common_alert_shutdown_imminent
AlertType.SUSPEND_IN_PROGRESS ->
R.string.omnipod_common_alert_delivery_suspended
AlertType.SUSPEND_ENDED ->
R.string.omnipod_common_alert_delivery_suspended
else ->
R.string.omnipod_common_alert_unknown_alert
}
@ -629,16 +633,8 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
private fun updateSuspendDeliveryButton() {
// If the Pod is currently suspended, we show the Resume delivery button instead.
if (isSuspendDeliveryButtonEnabled() &&
podStateManager.isPodRunning &&
(!podStateManager.isSuspended || commandQueue.isCustomCommandInQueue(CommandSuspendDelivery::class.java))
) {
buttonBinding.buttonSuspendDelivery.visibility = View.VISIBLE
buttonBinding.buttonSuspendDelivery.isEnabled =
podStateManager.isPodRunning && !podStateManager.isSuspended && isQueueEmpty()
} else {
buttonBinding.buttonSuspendDelivery.visibility = View.GONE
}
// disable the 'suspendDelivery' button.
buttonBinding.buttonSuspendDelivery.visibility = View.GONE
}
private fun updateSetTimeButton() {
@ -654,10 +650,6 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
return sp.getBoolean(R.string.omnipod_common_preferences_automatically_silence_alerts, false)
}
private fun isSuspendDeliveryButtonEnabled(): Boolean {
return sp.getBoolean(R.string.key_omnipod_common_suspend_delivery_button_enabled, false)
}
private fun displayErrorDialog(title: String, message: String, withSound: Boolean) {
context?.let {
ErrorHelperActivity.runAlarm(it, message, title, if (withSound) R.raw.boluserror else 0)

View file

@ -47,4 +47,5 @@
<string name="omnipod_common_history_tbr_value">Rate: %1$.2f U, duration: %2$d minutes</string>
<string name="omnipod_common_history_bolus_value">%1$.2f U</string>
<string name="dash_bolusdelivering">Delivering %1$.2f U</string>
<string name="omnipod_common_alert_delivery_suspended">Insulin delivery is suspended</string>
</resources>

View file

@ -104,12 +104,7 @@
android:key="@string/key_common_preferences_category_other_settings"
android:title="@string/omnipod_common_preferences_category_other"
app:initialExpandedChildrenCount="0">
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_omnipod_common_suspend_delivery_button_enabled"
android:title="@string/omnipod_common_preferences_suspend_delivery_button_enabled" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_omnipod_common_time_change_event_enabled"

View file

@ -85,7 +85,7 @@ class ProgramAlertsCommandTest {
false,
AlertTrigger.TimerTrigger(4079.toShort()),
BeepType.FOUR_TIMES_BIP_BEEP,
BeepRepetitionType.XXX2
BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN
)
)

View file

@ -0,0 +1,20 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType
import org.apache.commons.codec.DecoderException
import org.apache.commons.codec.binary.Hex
import org.junit.Assert
import org.junit.Test
class SuspendDeliveryCommandTest {
@Test @Throws(DecoderException::class) fun testSuspendDelivery() {
val encoded = SuspendDeliveryCommand.Builder()
.setUniqueId(37879811)
.setSequenceNumber(0.toShort())
.setNonce(1229869870)
.setBeepType(BeepType.LONG_SINGLE_BEEP)
.build()
.encoded
Assert.assertArrayEquals(Hex.decodeHex("0242000300131f05494e532e67190a494e532e680000140302811f"), encoded)
}
}

View file

@ -85,10 +85,11 @@ public class RileyLinkBLE {
aapsLogger.debug(LTag.PUMPBTCOMM, "Response Count is " + ByteUtil.shortHexString(characteristic.getValue()));
}
}
if (radioResponseCountNotified != null) {
radioResponseCountNotified.run();
if (characteristic.getUuid().equals(UUID.fromString(GattAttributes.CHARA_RADIO_RESPONSE_COUNT))) {
if (radioResponseCountNotified != null) {
radioResponseCountNotified.run();
}
}
orangeLink.onCharacteristicChanged(characteristic);
}