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 package info.nightscout.androidaps.activities
import android.annotation.SuppressLint
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.os.Bundle import android.os.Bundle
import android.text.Editable 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.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 { Thread {
val tdds = tddCalculator.stats() val tdds = tddCalculator.stats()
runOnUiThread { binding.tdds.text = tdds } runOnUiThread { binding.tdds.text = tdds }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -47,8 +47,8 @@ class TempBasalDialog : DialogFragmentWithDate() {
override fun onSaveInstanceState(savedInstanceState: Bundle) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState) super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("duration", binding.duration.value) savedInstanceState.putDouble("duration", binding.duration.value)
savedInstanceState.putDouble("basalpercentinput", binding.basalpercentinput.value) savedInstanceState.putDouble("basalPercentInput", binding.basalPercentInput.value)
savedInstanceState.putDouble("basalabsoluteinput", binding.basalabsoluteinput.value) savedInstanceState.putDouble("basalAbsoluteInput", binding.basalAbsoluteInput.value)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
@ -67,10 +67,10 @@ class TempBasalDialog : DialogFragmentWithDate() {
val maxTempPercent = pumpDescription.maxTempPercent.toDouble() val maxTempPercent = pumpDescription.maxTempPercent.toDouble()
val tempPercentStep = pumpDescription.tempPercentStep.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) ?: 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) ?: profile.getBasal(), 0.0, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, DecimalFormat("0.00"), true, binding.okcancel.ok)
val tempDurationStep = pumpDescription.tempDurationStep.toDouble() val tempDurationStep = pumpDescription.tempDurationStep.toDouble()
@ -97,17 +97,17 @@ class TempBasalDialog : DialogFragmentWithDate() {
if (_binding == null) return false if (_binding == null) return false
var percent = 0 var percent = 0
var absolute = 0.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 profile = profileFunction.getProfile() ?: return false
val actions: LinkedList<String> = LinkedList() val actions: LinkedList<String> = LinkedList()
if (isPercentPump) { if (isPercentPump) {
val basalPercentInput = SafeParse.stringToInt(binding.basalpercentinput.text) val basalPercentInput = SafeParse.stringToInt(binding.basalPercentInput.text)
percent = constraintChecker.applyBasalPercentConstraints(Constraint(basalPercentInput), profile).value() percent = constraintChecker.applyBasalPercentConstraints(Constraint(basalPercentInput), profile).value()
actions.add(rh.gs(R.string.tempbasal_label) + ": $percent%") actions.add(rh.gs(R.string.tempbasal_label) + ": $percent%")
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes)) actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes))
if (percent != basalPercentInput) actions.add(rh.gs(R.string.constraintapllied)) if (percent != basalPercentInput) actions.add(rh.gs(R.string.constraintapllied))
} else { } else {
val basalAbsoluteInput = SafeParse.stringToDouble(binding.basalabsoluteinput.text) val basalAbsoluteInput = SafeParse.stringToDouble(binding.basalAbsoluteInput.text)
absolute = constraintChecker.applyBasalConstraints(Constraint(basalAbsoluteInput), profile).value() 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.tempbasal_label) + ": " + rh.gs(R.string.pump_basebasalrate, absolute))
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes)) 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 { override fun submit(): Boolean {
if (_binding == null) return false if (_binding == null) return false
val pumpDescription = activePlugin.activePump.pumpDescription 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 carbs = SafeParse.stringToInt(binding.carbs.text)
val recordOnlyChecked = binding.recordOnly.isChecked val recordOnlyChecked = binding.recordOnly.isChecked
val actions: LinkedList<String?> = LinkedList() 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)), { OKDialog.showConfirmation(activity, rh.gs(R.string.overview_treatment_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
val action = when { val action = when {
insulinAfterConstraints.equals(0.0) -> Action.CARBS insulinAfterConstraints.equals(0.0) -> Action.CARBS
carbsAfterConstraints.equals(0) -> Action.BOLUS carbsAfterConstraints == 0 -> Action.BOLUS
else -> Action.TREATMENT else -> Action.TREATMENT
} }
val detailedBolusInfo = DetailedBolusInfo() val detailedBolusInfo = DetailedBolusInfo()

View file

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

View file

@ -109,7 +109,7 @@ class ObjectivesExamDialog : DaggerDialogFragment() {
task.answered = result task.answered = result
if (!result) { if (!result) {
task.disabledTo = dateUtil.now() + T.hours(1).msecs() 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 } else task.disabledTo = 0
updateGui() updateGui()
rxBus.send(EventObjectivesUpdateGui()) rxBus.send(EventObjectivesUpdateGui())

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -664,13 +664,15 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
IobTotal bolusIob = iobCobCalculator.calculateIobFromBolus().round(); IobTotal bolusIob = iobCobCalculator.calculateIobFromBolus().round();
IobTotal basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round(); IobTotal basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round();
iobSum = DecimalFormatter.INSTANCE.to2Decimal(bolusIob.iob + basalIob.basaliob); iobSum = DecimalFormatter.INSTANCE.to2Decimal(bolusIob.getIob() + basalIob.getBasaliob());
iobDetail = "(" + DecimalFormatter.INSTANCE.to2Decimal(bolusIob.iob) + "|" + DecimalFormatter.INSTANCE.to2Decimal(basalIob.basaliob) + ")"; iobDetail =
"(" + DecimalFormatter.INSTANCE.to2Decimal(bolusIob.getIob()) + "|" + DecimalFormatter.INSTANCE.to2Decimal(basalIob.getBasaliob()) + ")";
cobString = iobCobCalculator.getCobInfo(false, "WatcherUpdaterService").generateCOBString(); cobString = iobCobCalculator.getCobInfo(false, "WatcherUpdaterService").generateCOBString();
currentBasal = generateBasalString(); currentBasal = generateBasalString();
//bgi //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); bgiString = "" + ((bgi >= 0) ? "+" : "") + DecimalFormatter.INSTANCE.to1Decimal(bgi);
status = generateStatusString(profile, currentBasal, iobSum, iobDetail, bgiString); status = generateStatusString(profile, currentBasal, iobSum, iobDetail, bgiString);

View file

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

View file

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

View file

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

View file

@ -32,7 +32,7 @@ class CommandBolus(
} }
override fun status(): String { override fun status(): String {
return (if (detailedBolusInfo.insulin > 0) "BOLUS " + rh.gs(R.string.formatinsulinunits, detailedBolusInfo.insulin) else "") + return (if (detailedBolusInfo.insulin > 0) rh.gs(R.string.bolus_u_min, detailedBolusInfo.insulin) else "") +
if (detailedBolusInfo.carbs > 0) "CARBS " + rh.gs(R.string.format_carbs, detailedBolusInfo.carbs.toInt()) 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 package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
@ -19,5 +20,5 @@ class CommandCancelExtendedBolus constructor(
callback?.result(r)?.run() 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 package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
@ -20,5 +21,5 @@ class CommandCancelTempBasal(
callback?.result(r)?.run() 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 package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
@ -21,5 +22,5 @@ class CommandExtendedBolus constructor(
callback?.result(r)?.run() 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 package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
@ -23,5 +24,5 @@ class CommandInsightSetTBROverNotification constructor(
} }
@Suppress("SpellCheckingInspection") @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 package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Dana import info.nightscout.androidaps.interfaces.Dana
import info.nightscout.androidaps.interfaces.Diaconn 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 package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Dana import info.nightscout.androidaps.interfaces.Dana
import info.nightscout.androidaps.interfaces.Diaconn 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 package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
@ -20,5 +21,5 @@ class CommandLoadTDDs(
callback?.result(r)?.run() 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() 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 package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Dana import info.nightscout.androidaps.interfaces.Dana
import info.nightscout.androidaps.interfaces.Diaconn 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 package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.androidaps.queue.Callback 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 package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.androidaps.queue.Callback 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 package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.PumpSync import info.nightscout.androidaps.interfaces.PumpSync
@ -26,5 +27,5 @@ class CommandTempBasalAbsolute(
callback?.result(r)?.run() 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 package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.PumpSync import info.nightscout.androidaps.interfaces.PumpSync
@ -30,5 +31,5 @@ class CommandTempBasalPercent(
callback?.result(r)?.run() 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 android.view.LayoutInflater
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
class NumberPickerVertical : NumberPicker { class NumberPickerVertical(context: Context, attrs: AttributeSet? = null) : NumberPicker(context, attrs) {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
override fun inflate(context: Context) { override fun inflate(context: Context) {
LayoutInflater.from(context).inflate(R.layout.number_picker_layout_vertical, this, true) LayoutInflater.from(context).inflate(R.layout.number_picker_layout_vertical, this, true)

View file

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

View file

@ -4,6 +4,7 @@ import android.content.Context
import android.os.PowerManager import android.os.PowerManager
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.TestBaseWithProfile import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.androidaps.TestPumpPlugin import info.nightscout.androidaps.TestPumpPlugin
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
@ -21,6 +22,7 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.junit.Assert import org.junit.Assert
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.mockito.ArgumentMatchers
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito import org.mockito.Mockito
@ -41,6 +43,7 @@ class QueueThreadTest : TestBaseWithProfile() {
} }
if (it is CommandTempBasalAbsolute) { if (it is CommandTempBasalAbsolute) {
it.activePlugin = activePlugin it.activePlugin = activePlugin
it.rh = rh
} }
} }
} }
@ -75,6 +78,7 @@ class QueueThreadTest : TestBaseWithProfile() {
val percentageConstraint = Constraint(0) val percentageConstraint = Constraint(0)
Mockito.`when`(constraintChecker.applyBasalPercentConstraints(anyObject(), anyObject())) Mockito.`when`(constraintChecker.applyBasalPercentConstraints(anyObject(), anyObject()))
.thenReturn(percentageConstraint) .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) sut = QueueThread(commandQueue, context, aapsLogger, rxBus, activePlugin, rh, sp)
} }

View file

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

View file

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

View file

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

View file

@ -164,7 +164,7 @@ class ProfileViewerDialog : DaggerDialogFragment() {
binding.date.text = date binding.date.text = date
binding.ic.text = it.getIcList(rh, dateUtil) binding.ic.text = it.getIcList(rh, dateUtil)
binding.isf.text = it.getIsfList(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.target.text = it.getTargetList(rh, dateUtil)
binding.basalGraph.show(it) 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) { synchronized(dataLock) {
var i = autosensDataTable.size() - 1 var i = autosensDataTable.size() - 1
while (i >= 0 && count < valuesToProcess) { while (i >= 0 && count < valuesToProcess) {
if (autosensDataTable.valueAt(i).failoverToMinAbsorbtionRate) sum++ if (autosensDataTable.valueAt(i).failOverToMinAbsorptionRate) sum++
count++ count++
i-- 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() { override fun updateEditText() {
if (value == 0.0 && !allowZero) editText.setText("") if (currentValue == 0.0 && !allowZero) editText?.setText("")
else { else {
if (focused) editText.setText(DecimalFormat("0").format(value)) if (focused) editText?.setText(DecimalFormat("0").format(currentValue))
else { else {
val hours = (value / 60).toInt() val hours = (currentValue / 60).toInt()
val minutes = (value - hours * 60).toInt() val minutes = (currentValue - hours * 60).toInt()
val formatted = val formatted =
if (hours != 0) String.format(context.getString(R.string.format_hour_minute), hours, minutes) if (hours != 0) String.format(context.getString(R.string.format_hour_minute), hours, minutes)
else DecimalFormat("0").format(value) else DecimalFormat("0").format(currentValue)
editText.setText(formatted) 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="basal_value">Basal value</string>
<string name="nsclient_version_does_not_match">NSClient version doesn\'t match to AndroidAPS. Please update.</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_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="keepalive_basal_outdated" comment="26 characters max for translation">KeepAlive. Basal outdated.</string>
<string name="sms" comment="26 characters max for translation">SMS</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="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="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="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"> <plurals name="days">
<item quantity="one">%1$d day</item> <item quantity="one">%1$d day</item>

View file

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

View file

@ -207,10 +207,12 @@ public abstract class AbstractDanaRExecutionService extends DaggerService {
} }
} }
} else { } else {
ToastUtils.showToastInUiThread(context.getApplicationContext(), rh.gs(R.string.nobtadapter)); ToastUtils.INSTANCE.showToastInUiThread(context.getApplicationContext(),
rh.gs(R.string.nobtadapter));
} }
if (mBTDevice == null) { 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 info.nightscout.androidaps.danars.encryption.BleEncryption
import javax.inject.Inject import javax.inject.Inject
@Suppress("MemberVisibilityCanBePrivate")
class DanaRSPacketGeneralInitialScreenInformation( class DanaRSPacketGeneralInitialScreenInformation(
injector: HasAndroidInjector injector: HasAndroidInjector
) : DanaRSPacket(injector) { ) : DanaRSPacket(injector) {
@Inject lateinit var danaPump: DanaPump @Inject lateinit var danaPump: DanaPump
var isTempBasalInProgress = false
var isExtendedInProgress = false
var isDualBolusInProgress = false
init { init {
opCode = BleEncryption.DANAR_PACKET__OPCODE_REVIEW__INITIAL_SCREEN_INFORMATION opCode = BleEncryption.DANAR_PACKET__OPCODE_REVIEW__INITIAL_SCREEN_INFORMATION
aapsLogger.debug(LTag.PUMPCOMM, "New message") aapsLogger.debug(LTag.PUMPCOMM, "New message")
@ -22,42 +27,22 @@ class DanaRSPacketGeneralInitialScreenInformation(
failed = true failed = true
return return
} else failed = false } else failed = false
var dataIndex = DATA_START val status = intFromBuff(data, 0, 1)
var dataSize = 1
val status = byteArrayToInt(getBytes(data, dataIndex, dataSize))
danaPump.pumpSuspended = status and 0x01 == 0x01 danaPump.pumpSuspended = status and 0x01 == 0x01
val isTempBasalInProgress = status and 0x10 == 0x10 isTempBasalInProgress = status and 0x10 == 0x10
val isExtendedInProgress = status and 0x04 == 0x04 isExtendedInProgress = status and 0x04 == 0x04
val isDualBolusInProgress = status and 0x08 == 0x08 isDualBolusInProgress = status and 0x08 == 0x08
dataIndex += dataSize danaPump.dailyTotalUnits = intFromBuff(data, 1, 2) / 100.0
dataSize = 2 danaPump.maxDailyTotalUnits = intFromBuff(data, 3, 2) / 100
danaPump.dailyTotalUnits = byteArrayToInt(getBytes(data, dataIndex, dataSize)) / 100.0 danaPump.reservoirRemainingUnits = intFromBuff(data, 5, 2) / 100.0
dataIndex += dataSize danaPump.currentBasal = intFromBuff(data, 7, 2) / 100.0
dataSize = 2 val tempBasalPercent = intFromBuff(data, 9, 1)
danaPump.maxDailyTotalUnits = (byteArrayToInt(getBytes(data, dataIndex, dataSize)) / 100.0).toInt() danaPump.batteryRemaining = intFromBuff(data, 10, 1)
dataIndex += dataSize val extendedBolusAbsoluteRate = intFromBuff(data, 11, 2) / 100.0
dataSize = 2 danaPump.iob = intFromBuff(data, 13, 2) / 100.0
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
if (data.size >= 18) { if (data.size >= 18) {
//protocol 10+ //protocol 10+
dataIndex += dataSize danaPump.errorState = DanaPump.ErrorState[intFromBuff(data, 15, 1)]
dataSize = 1
danaPump.errorState = DanaPump.ErrorState[byteArrayToInt(getBytes(data, dataIndex, dataSize))]
?: DanaPump.ErrorState.NONE ?: DanaPump.ErrorState.NONE
aapsLogger.debug(LTag.PUMPCOMM, "ErrorState: " + danaPump.errorState.name) aapsLogger.debug(LTag.PUMPCOMM, "ErrorState: " + danaPump.errorState.name)
} }

View file

@ -338,7 +338,9 @@ class DanaRSService : DaggerService() {
fun tempBasal(percent: Int, durationInHours: Int): Boolean { fun tempBasal(percent: Int, durationInHours: Int): Boolean {
if (!isConnected) return false if (!isConnected) return false
if (danaPump.isTempBasalInProgress) { val status = DanaRSPacketGeneralInitialScreenInformation(injector)
sendMessage(status)
if (status.isTempBasalInProgress) {
rxBus.send(EventPumpStatusChanged(rh.gs(R.string.stoppingtempbasal))) rxBus.send(EventPumpStatusChanged(rh.gs(R.string.stoppingtempbasal)))
sendMessage(DanaRSPacketBasalSetCancelTemporaryBasal(injector)) sendMessage(DanaRSPacketBasalSetCancelTemporaryBasal(injector))
SystemClock.sleep(500) SystemClock.sleep(500)
@ -348,6 +350,7 @@ class DanaRSService : DaggerService() {
sendMessage(msgTBR) sendMessage(msgTBR)
SystemClock.sleep(200) SystemClock.sleep(200)
loadEvents() loadEvents()
SystemClock.sleep(4500)
val tbr = pumpSync.expectedPumpState().temporaryBasal val tbr = pumpSync.expectedPumpState().temporaryBasal
danaPump.fromTemporaryBasal(tbr) danaPump.fromTemporaryBasal(tbr)
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)) rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING))
@ -355,7 +358,9 @@ class DanaRSService : DaggerService() {
} }
fun highTempBasal(percent: Int): Boolean { 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))) rxBus.send(EventPumpStatusChanged(rh.gs(R.string.stoppingtempbasal)))
sendMessage(DanaRSPacketBasalSetCancelTemporaryBasal(injector)) sendMessage(DanaRSPacketBasalSetCancelTemporaryBasal(injector))
SystemClock.sleep(500) SystemClock.sleep(500)
@ -364,6 +369,7 @@ class DanaRSService : DaggerService() {
val msgTBR = DanaRSPacketAPSBasalSetTemporaryBasal(injector, percent) val msgTBR = DanaRSPacketAPSBasalSetTemporaryBasal(injector, percent)
sendMessage(msgTBR) sendMessage(msgTBR)
loadEvents() loadEvents()
SystemClock.sleep(4500)
val tbr = pumpSync.expectedPumpState().temporaryBasal val tbr = pumpSync.expectedPumpState().temporaryBasal
danaPump.fromTemporaryBasal(tbr) danaPump.fromTemporaryBasal(tbr)
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)) rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING))
@ -375,7 +381,9 @@ class DanaRSService : DaggerService() {
aapsLogger.error(LTag.PUMPCOMM, "Wrong duration param") aapsLogger.error(LTag.PUMPCOMM, "Wrong duration param")
return false return false
} }
if (danaPump.isTempBasalInProgress) { val status = DanaRSPacketGeneralInitialScreenInformation(injector)
sendMessage(status)
if (status.isTempBasalInProgress) {
rxBus.send(EventPumpStatusChanged(rh.gs(R.string.stoppingtempbasal))) rxBus.send(EventPumpStatusChanged(rh.gs(R.string.stoppingtempbasal)))
sendMessage(DanaRSPacketBasalSetCancelTemporaryBasal(injector)) sendMessage(DanaRSPacketBasalSetCancelTemporaryBasal(injector))
SystemClock.sleep(500) SystemClock.sleep(500)
@ -384,6 +392,7 @@ class DanaRSService : DaggerService() {
val msgTBR = DanaRSPacketAPSBasalSetTemporaryBasal(injector, percent) val msgTBR = DanaRSPacketAPSBasalSetTemporaryBasal(injector, percent)
sendMessage(msgTBR) sendMessage(msgTBR)
loadEvents() loadEvents()
SystemClock.sleep(4500)
val tbr = pumpSync.expectedPumpState().temporaryBasal val tbr = pumpSync.expectedPumpState().temporaryBasal
aapsLogger.debug(LTag.PUMPCOMM, "Expected TBR found: $tbr") aapsLogger.debug(LTag.PUMPCOMM, "Expected TBR found: $tbr")
danaPump.fromTemporaryBasal(tbr) danaPump.fromTemporaryBasal(tbr)
@ -397,6 +406,7 @@ class DanaRSService : DaggerService() {
val msgCancel = DanaRSPacketBasalSetCancelTemporaryBasal(injector) val msgCancel = DanaRSPacketBasalSetCancelTemporaryBasal(injector)
sendMessage(msgCancel) sendMessage(msgCancel)
loadEvents() loadEvents()
SystemClock.sleep(4500)
val tbr = pumpSync.expectedPumpState().temporaryBasal val tbr = pumpSync.expectedPumpState().temporaryBasal
danaPump.fromTemporaryBasal(tbr) danaPump.fromTemporaryBasal(tbr)
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)) rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING))
@ -410,6 +420,7 @@ class DanaRSService : DaggerService() {
sendMessage(msgExtended) sendMessage(msgExtended)
SystemClock.sleep(200) SystemClock.sleep(200)
loadEvents() loadEvents()
SystemClock.sleep(4500)
val eb = pumpSync.expectedPumpState().extendedBolus val eb = pumpSync.expectedPumpState().extendedBolus
danaPump.fromExtendedBolus(eb) danaPump.fromExtendedBolus(eb)
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)) rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING))
@ -422,6 +433,7 @@ class DanaRSService : DaggerService() {
val msgStop = DanaRSPacketBolusSetExtendedBolusCancel(injector) val msgStop = DanaRSPacketBolusSetExtendedBolusCancel(injector)
sendMessage(msgStop) sendMessage(msgStop)
loadEvents() loadEvents()
SystemClock.sleep(4500)
val eb = pumpSync.expectedPumpState().extendedBolus val eb = pumpSync.expectedPumpState().extendedBolus
danaPump.fromExtendedBolus(eb) danaPump.fromExtendedBolus(eb)
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)) 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 // prevent setBasal requests
return true 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 running = podStateManager.basalProgram
val equal = (mapProfileToBasalProgram(profile) == running) val equal = (mapProfileToBasalProgram(profile) == running)
aapsLogger.info(LTag.PUMP, "set: $equal. profile=$profile, running=$running") aapsLogger.info(LTag.PUMP, "set: $equal. profile=$profile, running=$running")
@ -1089,8 +1092,6 @@ class OmnipodDashPumpPlugin @Inject constructor(
return when (customCommand) { return when (customCommand) {
is CommandSilenceAlerts -> is CommandSilenceAlerts ->
silenceAlerts() silenceAlerts()
is CommandSuspendDelivery ->
suspendDelivery()
is CommandResumeDelivery -> is CommandResumeDelivery ->
resumeDelivery() resumeDelivery()
is CommandDeactivatePod -> is CommandDeactivatePod ->
@ -1101,7 +1102,8 @@ class OmnipodDashPumpPlugin @Inject constructor(
updateAlertConfiguration() updateAlertConfiguration()
is CommandPlayTestBeep -> is CommandPlayTestBeep ->
playTestBeep() playTestBeep()
is CommandDisableSuspendAlerts ->
disableSuspendAlerts()
else -> { else -> {
aapsLogger.warn(LTag.PUMP, "Unsupported custom command: " + customCommand.javaClass.name) aapsLogger.warn(LTag.PUMP, "Unsupported custom command: " + customCommand.javaClass.name)
PumpEnactResult(injector).success(false).enacted(false).comment( 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 } ?: PumpEnactResult(injector).success(false).enacted(false).comment("No active alerts") // TODO i18n
} }
private fun suspendDelivery(): PumpEnactResult { private fun disableSuspendAlerts(): PumpEnactResult {
return executeProgrammingCommand( val alerts = listOf(
historyEntry = history.createRecord(OmnipodCommandType.SUSPEND_DELIVERY), AlertConfiguration(
command = omnipodManager.suspendDelivery(hasBasalBeepEnabled()) AlertType.SUSPEND_ENDED,
.filter { podEvent -> podEvent.isCommandSent() } enabled = false,
.map { durationInMinutes = 0,
pumpSyncTempBasal( autoOff = false,
0.0, AlertTrigger.TimerTrigger(
PodConstants.MAX_POD_LIFETIME.toMinutes(), 0
PumpSync.TemporaryBasalType.PUMP_SUSPEND ),
) BeepType.FOUR_TIMES_BIP_BEEP,
} BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN
.ignoreElements(), ),
pre = observeDeliveryActive(), )
).doFinally { val ret = executeProgrammingCommand(
notifyOnUnconfirmed( historyEntry = history.createRecord(OmnipodCommandType.CONFIGURE_ALERTS),
Notification.PUMP_ERROR, command = omnipodManager.programAlerts(alerts).ignoreElements(),
"Unconfirmed suspendDelivery command. Please refresh pod status", ).toPumpEnactResult()
R.raw.boluserror if (ret.success && ret.enacted) {
) podStateManager.suspendAlertsEnabled = false
}.toPumpEnactResult() }
return ret
} }
private fun observeDeliveryActive(): Completable = Completable.defer { private fun observeDeliveryActive(): Completable = Completable.defer {
@ -1249,7 +1252,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
expiryAlertDelay.toMinutes().toShort() expiryAlertDelay.toMinutes().toShort()
), ),
BeepType.FOUR_TIMES_BIP_BEEP, BeepType.FOUR_TIMES_BIP_BEEP,
BeepRepetitionType.XXX2 BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN
) )
) )
return executeProgrammingCommand( return executeProgrammingCommand(
@ -1369,9 +1372,11 @@ class OmnipodDashPumpPlugin @Inject constructor(
) )
podStateManager.tempBasal = null podStateManager.tempBasal = null
rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED)) 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 -> { OmnipodCommandType.SET_BASAL_PROFILE -> {
@ -1394,6 +1399,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
rxBus.send(EventDismissNotification(Notification.FAILED_UPDATE_PROFILE)) rxBus.send(EventDismissNotification(Notification.FAILED_UPDATE_PROFILE))
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS)) rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS))
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TIME_OUT_OF_SYNC)) 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
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_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_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.response.*
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager 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 info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.functions.Action import io.reactivex.functions.Action
@ -281,7 +283,7 @@ class OmnipodDashManagerImpl @Inject constructor(
.setUniqueId(podStateManager.uniqueId!!.toInt()) .setUniqueId(podStateManager.uniqueId!!.toInt())
.setSequenceNumber(podStateManager.messageSequenceNumber) .setSequenceNumber(podStateManager.messageSequenceNumber)
.setNonce(NONCE) .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()) .setDelayBetweenPulsesInEighthSeconds(podStateManager.primePulseRate!!.toByte())
.setProgramReminder(ProgramReminder(atStart = false, atEnd = false, atInterval = 0)) .setProgramReminder(ProgramReminder(atStart = false, atEnd = false, atInterval = 0))
.build(), .build(),
@ -383,7 +385,7 @@ class OmnipodDashManagerImpl @Inject constructor(
) )
observables.add( observables.add(
observeSendProgramBolusCommand( 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(), podStateManager.primePulseRate!!.toByte(),
confirmationBeeps = false, confirmationBeeps = false,
completionBeeps = false completionBeeps = false
@ -437,7 +439,7 @@ class OmnipodDashManagerImpl @Inject constructor(
userExpiryAlertDelay.toMinutes().toShort() userExpiryAlertDelay.toMinutes().toShort()
), ),
BeepType.FOUR_TIMES_BIP_BEEP, 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( return Observable.concat(
observePodRunning, observePodRunning,
observeConnectToPod, observeConnectToPod,
observeSendStopDeliveryCommand(StopDeliveryCommand.DeliveryType.ALL, hasBasalBeepEnabled) observeSuspendDeliveryCommand(hasBasalBeepEnabled)
).interceptPodEvents() ).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> { 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()) 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 { override fun toString(): String {
return "ProgramAlertsCommand{" + return "ProgramAlertsCommand{" +
"alertConfigurations=" + alertConfigurations + "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 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 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 XXX5(0x08.toByte()); // Lump of coal alert
} }

View file

@ -77,6 +77,7 @@ interface OmnipodDashPodStateManager {
var basalProgram: BasalProgram? var basalProgram: BasalProgram?
val activeCommand: ActiveCommand? val activeCommand: ActiveCommand?
val lastBolus: LastBolus? val lastBolus: LastBolus?
var suspendAlertsEnabled: Boolean
fun increaseMessageSequenceNumber() fun increaseMessageSequenceNumber()
fun increaseEapAkaSequenceNumber(): ByteArray 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.DefaultStatusResponse
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.SetUniqueIdResponse 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.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.Completable import io.reactivex.Completable
import io.reactivex.Maybe import io.reactivex.Maybe
@ -224,6 +225,13 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
store() store()
} }
override var suspendAlertsEnabled: Boolean
get() = podState.suspendAlertsEnabled
set(enabled) {
podState.suspendAlertsEnabled = enabled
store()
}
override val lastStatusResponseReceived: Long override val lastStatusResponseReceived: Long
get() = podState.lastStatusResponseReceived get() = podState.lastStatusResponseReceived
@ -340,7 +348,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
private fun updateLastBolusFromResponse(bolusPulsesRemaining: Short) { private fun updateLastBolusFromResponse(bolusPulsesRemaining: Short) {
podState.lastBolus?.run { 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 this.bolusUnitsRemaining = remainingUnits
if (remainingUnits == 0.0) { if (remainingUnits == 0.0) {
this.deliveryComplete = true this.deliveryComplete = true
@ -713,6 +721,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
var timeZoneOffset: Int? = null var timeZoneOffset: Int? = null
var timeZoneUpdated: Long? = null var timeZoneUpdated: Long? = null
var alarmSynced: Boolean = false var alarmSynced: Boolean = false
var suspendAlertsEnabled: Boolean = false
var bleVersion: SoftwareVersion? = null var bleVersion: SoftwareVersion? = null
var firmwareVersion: SoftwareVersion? = null var firmwareVersion: SoftwareVersion? = null

View file

@ -439,6 +439,10 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
R.string.omnipod_common_alert_expiration_advisory R.string.omnipod_common_alert_expiration_advisory
AlertType.AUTO_OFF -> AlertType.AUTO_OFF ->
R.string.omnipod_common_alert_shutdown_imminent 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 -> else ->
R.string.omnipod_common_alert_unknown_alert R.string.omnipod_common_alert_unknown_alert
} }
@ -629,16 +633,8 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
private fun updateSuspendDeliveryButton() { private fun updateSuspendDeliveryButton() {
// If the Pod is currently suspended, we show the Resume delivery button instead. // If the Pod is currently suspended, we show the Resume delivery button instead.
if (isSuspendDeliveryButtonEnabled() && // disable the 'suspendDelivery' button.
podStateManager.isPodRunning && buttonBinding.buttonSuspendDelivery.visibility = View.GONE
(!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
}
} }
private fun updateSetTimeButton() { private fun updateSetTimeButton() {
@ -654,10 +650,6 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
return sp.getBoolean(R.string.omnipod_common_preferences_automatically_silence_alerts, false) 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) { private fun displayErrorDialog(title: String, message: String, withSound: Boolean) {
context?.let { context?.let {
ErrorHelperActivity.runAlarm(it, message, title, if (withSound) R.raw.boluserror else 0) 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_tbr_value">Rate: %1$.2f U, duration: %2$d minutes</string>
<string name="omnipod_common_history_bolus_value">%1$.2f U</string> <string name="omnipod_common_history_bolus_value">%1$.2f U</string>
<string name="dash_bolusdelivering">Delivering %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> </resources>

View file

@ -105,11 +105,6 @@
android:title="@string/omnipod_common_preferences_category_other" android:title="@string/omnipod_common_preferences_category_other"
app:initialExpandedChildrenCount="0"> 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 <SwitchPreference
android:defaultValue="true" android:defaultValue="true"
android:key="@string/key_omnipod_common_time_change_event_enabled" android:key="@string/key_omnipod_common_time_change_event_enabled"

View file

@ -85,7 +85,7 @@ class ProgramAlertsCommandTest {
false, false,
AlertTrigger.TimerTrigger(4079.toShort()), AlertTrigger.TimerTrigger(4079.toShort()),
BeepType.FOUR_TIMES_BIP_BEEP, 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())); aapsLogger.debug(LTag.PUMPBTCOMM, "Response Count is " + ByteUtil.shortHexString(characteristic.getValue()));
} }
} }
if (radioResponseCountNotified != null) { if (characteristic.getUuid().equals(UUID.fromString(GattAttributes.CHARA_RADIO_RESPONSE_COUNT))) {
radioResponseCountNotified.run(); if (radioResponseCountNotified != null) {
radioResponseCountNotified.run();
}
} }
orangeLink.onCharacteristicChanged(characteristic); orangeLink.onCharacteristicChanged(characteristic);
} }