Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/keys/src/main/kotlin/app/aaps/core/keys/IntKey.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ enum class IntKey(
ProtectionTypeBolus("bolus_protection", 0, 0, 5),
SafetyMaxCarbs("treatmentssafety_maxcarbs", 48, 1, 200),
LoopOpenModeMinChange("loop_openmode_min_change", 30, 0, 50, defaultedBySM = true),
LoopMinBgRecalcInterval("loop_min_bg_recalc_interval", 0, 0, 30),
ApsMaxSmbFrequency("smbinterval", 3, 1, 10, defaultedBySM = true, dependency = BooleanKey.ApsUseSmb),
ApsMaxMinutesOfBasalToLimitSmb("smbmaxminutes", 30, 15, 120, defaultedBySM = true, dependency = BooleanKey.ApsUseSmb),
ApsUamMaxMinutesOfBasalToLimitSmb("uamsmbmaxminutes", 30, 15, 120, defaultedBySM = true, dependency = BooleanKey.ApsUseSmb),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package app.aaps.plugins.aps.di

import app.aaps.plugins.aps.loop.CarbSuggestionReceiver
import app.aaps.plugins.aps.loop.LoopFragment
import app.aaps.plugins.aps.loop.LoopIntervalPreference
import dagger.Module
import dagger.android.ContributesAndroidInjector

Expand All @@ -10,4 +11,5 @@ import dagger.android.ContributesAndroidInjector
abstract class LoopModule {
@ContributesAndroidInjector abstract fun contributesLoopFragment(): LoopFragment
@ContributesAndroidInjector abstract fun contributesCarbSuggestionReceiver(): CarbSuggestionReceiver
@ContributesAndroidInjector abstract fun contributesLoopIntervalPreference(): LoopIntervalPreference
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package app.aaps.plugins.aps.loop

import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.TextView
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import app.aaps.core.interfaces.db.PersistenceLayer
import app.aaps.core.interfaces.resources.ResourceHelper
import app.aaps.core.interfaces.utils.DateUtil
import app.aaps.core.data.time.T
import app.aaps.core.keys.IntKey
import app.aaps.core.keys.interfaces.Preferences
import app.aaps.plugins.aps.R
import com.google.android.material.button.MaterialButtonToggleGroup
import dagger.android.HasAndroidInjector
import javax.inject.Inject

class LoopIntervalPreference(
ctx: Context,
attrs: AttributeSet? = null
) : Preference(ctx, attrs) {

@Inject lateinit var preferences: Preferences
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var persistenceLayer: PersistenceLayer

private val valueToButton = mapOf(
0 to R.id.btn_auto,
1 to R.id.btn_1,
2 to R.id.btn_2,
3 to R.id.btn_3,
4 to R.id.btn_4,
5 to R.id.btn_5
)
private val buttonToValue = valueToButton.entries.associate { it.value to it.key }

init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
layoutResource = R.layout.preference_loop_interval
key = IntKey.LoopMinBgRecalcInterval.key
}

override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
holder.isDividerAllowedAbove = false
holder.isDividerAllowedBelow = false
// Disable click on the row background so taps reach the toggle buttons instead
holder.itemView.isClickable = false

val titleView = holder.findViewById(R.id.title) as? TextView
val dynamicSummary = holder.findViewById(R.id.dynamic_summary) as? TextView
val warningText = holder.findViewById(R.id.warning_text) as? TextView
val helpText = holder.findViewById(R.id.help_text) as? TextView
val toggleGroup = holder.findViewById(R.id.toggle_group) as? MaterialButtonToggleGroup

titleView?.text = rh.gs(R.string.loop_min_bg_recalc_interval_title)
helpText?.text = rh.gs(R.string.loop_min_bg_recalc_interval_summary)

val currentValue = preferences.get(IntKey.LoopMinBgRecalcInterval)
updateDynamicText(dynamicSummary, warningText, currentValue)

toggleGroup?.clearOnButtonCheckedListeners()
valueToButton[currentValue]?.let { toggleGroup?.check(it) }

toggleGroup?.addOnButtonCheckedListener { _, checkedId, isChecked ->
if (!isChecked) return@addOnButtonCheckedListener
val minutes = buttonToValue[checkedId] ?: return@addOnButtonCheckedListener
preferences.put(IntKey.LoopMinBgRecalcInterval, minutes)
updateDynamicText(dynamicSummary, warningText, minutes)
}
}

private fun updateDynamicText(summaryView: TextView?, warningText: TextView?, minutes: Int) {
val now = dateUtil.now()
val dailyCount = persistenceLayer.getApsResults(now - T.hours(24).msecs(), now).size
val loopsPerDay = if (minutes == 0) dailyCount else 1440 / minutes

// 1st line: always white, describes selected mode
summaryView?.text = if (minutes == 0) rh.gs(R.string.loop_recalc_every_sensor_reading)
else rh.gs(R.string.loop_recalc_loops_per_day, loopsPerDay)

// 2nd line: red warning, shown if actual or projected high usage
if (dailyCount > 500) { // actual high usage
warningText?.text = rh.gs(R.string.loop_recalc_daily_count_warning, dailyCount)
warningText?.setTextColor(rh.gc(app.aaps.core.ui.R.color.warning))
} else if (minutes in 1..2) { // low data but high-rate selection
warningText?.text = rh.gs(R.string.loop_recalc_high_rate_warning)
warningText?.setTextColor(rh.gc(app.aaps.core.ui.R.color.warning))
} else {
warningText?.text = rh.gs(R.string.loop_recalc_daily_count, dailyCount)
warningText?.setTextColor(rh.gc(android.R.color.white))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,7 @@ class LoopPlugin @Inject constructor(
title = rh.gs(app.aaps.core.ui.R.string.loop)
initialExpandedChildrenCount = 0
addPreference(AdaptiveIntPreference(ctx = context, intKey = IntKey.LoopOpenModeMinChange, dialogMessage = R.string.loop_open_mode_min_change_summary, title = R.string.loop_open_mode_min_change))
addPreference(LoopIntervalPreference(context))
}
}

Expand Down
104 changes: 104 additions & 0 deletions plugins/aps/src/main/res/layout/preference_loop_interval.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingTop="12dp"
android:paddingBottom="12dp">

<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItem"
android:paddingBottom="8dp" />

<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/toggle_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:singleSelection="true"
app:selectionRequired="true">

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_auto"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="80dp"
android:text="No limit" />

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_1"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minWidth="0dp"
android:text="1" />

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_2"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minWidth="0dp"
android:text="2" />

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_3"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minWidth="0dp"
android:text="3" />

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_4"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minWidth="0dp"
android:text="4" />

<com.google.android.material.button.MaterialButton
android:id="@+id/btn_5"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minWidth="0dp"
android:text="5" />

</com.google.android.material.button.MaterialButtonToggleGroup>

<TextView
android:id="@+id/dynamic_summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textStyle="bold" />

<TextView
android:id="@+id/warning_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="4dp"
android:textAppearance="?android:attr/textAppearanceListItemSecondary" />

<TextView
android:id="@+id/help_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="4dp"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:alpha="0.7" />

</LinearLayout>
7 changes: 7 additions & 0 deletions plugins/aps/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@
<string name="loop_smb_set_by_pump_label">SMB set by pump</string>
<string name="loop_open_mode_min_change">Minimal request change [%]</string>
<string name="loop_open_mode_min_change_summary" formatted="false">Open Loop will popup new change request only if change is bigger than this value in %. Default value is 20%</string>
<string name="loop_min_bg_recalc_interval_title">Loop frequency limit, mins</string>
<string name="loop_min_bg_recalc_interval_summary">Sets the minimum time between full loop iterations. AAPS was designed for 5-minute CGM intervals — sensors that report more frequently can cause hundreds of extra loop cycles per day, increasing battery usage. 3 minutes or higher is recommended for most users.</string>
<string name="loop_recalc_daily_count">Actual usage: %1$d loops in the last 24h</string>
<string name="loop_recalc_daily_count_warning">Actual usage: %1$d loops in the last 24h. This may excessively drain battery. A higher frequency limit is recommended.</string>
<string name="loop_recalc_high_rate_warning">May noticeably reduce battery life.</string>
<string name="loop_recalc_every_sensor_reading">Recalculates every time a sensor reading arrives</string>
<string name="loop_recalc_loops_per_day">~%1$d loops/day</string>
<string name="fallback_smb_no_tdd">Fallback to SMB. Not enough TDD data.</string>
<string name="fallback_to_isf_no_tdd">Fallback to profile sensitivity. Not enough data. Reason: %1$s</string>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -419,8 +419,23 @@
private var scheduledHistoryPost: ScheduledFuture<*>? = null
private var scheduledEvent: EventNewHistoryData? = null

private var lastBgCalcTriggeredAt: Long = 0L

@Synchronized
private fun scheduleHistoryDataChange(event: EventNewHistoryData) {

Check failure on line 425 in plugins/main/src/main/kotlin/app/aaps/plugins/main/iob/iobCobCalculator/IobCobCalculatorPlugin.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 19 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=nightscout_AndroidAPS&issues=AZ07rHaOGOacgaJbIMG3&open=AZ07rHaOGOacgaJbIMG3&pullRequest=4651
val intervalMinutes = preferences.get(IntKey.LoopMinBgRecalcInterval)
if (intervalMinutes > 0 && event.reloadBgData && event.newestGlucoseValueTimestamp != null) {
val now = System.currentTimeMillis()
val intervalMs = intervalMinutes * 60 * 1000L - 10_000L
val timeSinceLastCalc = now - lastBgCalcTriggeredAt

if (timeSinceLastCalc < intervalMs) {
aapsLogger.debug(LTag.APS, "Throttled BG recalculation: ${timeSinceLastCalc / 1000}s since last, interval=${intervalMs / 1000}s")
return
}
lastBgCalcTriggeredAt = now
}

// if there is nothing scheduled or asking reload deeper to the past
if (scheduledEvent == null || event.oldDataTimestamp < (scheduledEvent?.oldDataTimestamp ?: 0L)) {
// cancel waiting task to prevent sending multiple posts
Expand Down Expand Up @@ -672,4 +687,4 @@
}
return total
}
}
}
Loading
Loading