From 21e7226d9c93a9b4a05c567ee794587a55cf7a13 Mon Sep 17 00:00:00 2001 From: Josh Slade Date: Sat, 20 May 2023 08:30:34 -0600 Subject: [PATCH 1/7] Improves Dash connecting --- .../omnipod/dash/driver/comm/session/Connection.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Connection.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Connection.kt index 358c87a4c6..7200aba9fc 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Connection.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Connection.kt @@ -56,6 +56,8 @@ class Connection( private val bluetoothManager: BluetoothManager? = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager? + private var _connectionWaitCond: ConnectionWaitCondition? = null + @Volatile var session: Session? = null @@ -65,6 +67,7 @@ class Connection( @Synchronized fun connect(connectionWaitCond: ConnectionWaitCondition) { aapsLogger.debug("Connecting connectionWaitCond=$connectionWaitCond") + _connectionWaitCond = connectionWaitCond podState.connectionAttempts++ podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTING val autoConnect = false @@ -80,6 +83,7 @@ class Connection( val before = SystemClock.elapsedRealtime() if (waitForConnection(connectionWaitCond) !is Connected) { podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.DISCONNECTED + _connectionWaitCond = null throw FailedToConnectException(podDevice.address) } val waitedMs = SystemClock.elapsedRealtime() - before @@ -92,6 +96,7 @@ class Connection( connectionWaitCond.timeoutMs = newTimeout } podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTED + _connectionWaitCond = null val discoverer = ServiceDiscoverer(aapsLogger, gatt, bleCommCallbacks, this) val discovered = discoverer.discoverServices(connectionWaitCond) @@ -200,7 +205,14 @@ class Connection( // This will be called from a different thread !!! override fun onConnectionLost(status: Int) { aapsLogger.info(LTag.PUMPBTCOMM, "Lost connection with status: $status") - disconnect(false) + // Check if waiting for connection, if so, stop waiting + _connectionWaitCond?.stopConnection?.let { + if (it.count > 0) { + _connectionWaitCond?.stopConnection?.countDown() + } + } + // BLE disconnected, so need to close gatt + disconnect(true) } companion object { From 4e346e7f998004251c466d5ebe4943c538e11cf0 Mon Sep 17 00:00:00 2001 From: Selcuk Kekec Date: Sat, 20 May 2023 08:36:19 -0600 Subject: [PATCH 2/7] Dexcom G7 integration for master --- app/src/main/AndroidManifest.xml | 1 + .../info/nightscout/androidaps/plugins/source/DexcomPlugin.kt | 3 ++- .../java/info/nightscout/androidaps/receivers/DataReceiver.kt | 3 +++ .../main/java/info/nightscout/androidaps/receivers/Intents.kt | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8fb0dea2f6..1049bb9862 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -144,6 +144,7 @@ + diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/DexcomPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/DexcomPlugin.kt index 7df9fe9729..a2f84ab42d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/DexcomPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/DexcomPlugin.kt @@ -227,7 +227,8 @@ class DexcomPlugin @Inject constructor( "com.dexcom.cgm.region1.mgdl", "com.dexcom.cgm.region1.mmol", "com.dexcom.cgm.region2.mgdl", "com.dexcom.cgm.region2.mmol", "com.dexcom.g6.region1.mmol", "com.dexcom.g6.region2.mgdl", - "com.dexcom.g6.region3.mgdl", "com.dexcom.g6.region3.mmol", "com.dexcom.g6" + "com.dexcom.g6.region3.mgdl", "com.dexcom.g6.region3.mmol", + "com.dexcom.g6", "com.dexcom.g7" ) const val PERMISSION = "com.dexcom.cgm.EXTERNAL_PERMISSION" } diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.kt b/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.kt index 446ccf39ee..14a0d84904 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.kt +++ b/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.kt @@ -64,6 +64,9 @@ open class DataReceiver : DaggerBroadcastReceiver() { Intents.DEXCOM_BG -> OneTimeWorkRequest.Builder(DexcomPlugin.DexcomWorker::class.java) .setInputData(dataWorker.storeInputData(bundle, intent.action)).build() + Intents.DEXCOM_G7_BG -> + OneTimeWorkRequest.Builder(DexcomPlugin.DexcomWorker::class.java) + .setInputData(dataWorker.storeInputData(bundle, intent.action)).build() Intents.AIDEX_NEW_BG_ESTIMATE -> OneTimeWorkRequest.Builder(AidexPlugin.AidexWorker::class.java) .setInputData(dataWorker.storeInputData(bundle, intent.action)).build() diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/Intents.kt b/app/src/main/java/info/nightscout/androidaps/receivers/Intents.kt index 8d09939b11..c13ead9150 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/Intents.kt +++ b/app/src/main/java/info/nightscout/androidaps/receivers/Intents.kt @@ -27,6 +27,7 @@ interface Intents { const val ACTION_REMOTE_CALIBRATION = "com.eveningoutpost.dexdrip.NewCalibration" const val GLIMP_BG = "it.ct.glicemia.ACTION_GLUCOSE_MEASURED" const val DEXCOM_BG = "com.dexcom.cgm.EXTERNAL_BROADCAST" + const val DEXCOM_G7_BG = "com.dexcom.g7.EXTERNAL_BROADCAST" const val EVERSENSE_BG = "com.senseonics.AndroidAPSEventSubscriber.BROADCAST" const val POCTECH_BG = "com.china.poctech.data" const val TOMATO_BG = "com.fanqies.tomatofn.BgEstimate" From 6515f803884517efe396dd88e99b2101b77db253 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sat, 20 May 2023 08:40:41 -0600 Subject: [PATCH 3/7] fix loading last value --- .../info/nightscout/androidaps/database/daos/GlucoseValueDao.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/src/main/java/info/nightscout/androidaps/database/daos/GlucoseValueDao.kt b/database/src/main/java/info/nightscout/androidaps/database/daos/GlucoseValueDao.kt index 4423868aca..e5cc439a0b 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/daos/GlucoseValueDao.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/daos/GlucoseValueDao.kt @@ -16,7 +16,7 @@ internal interface GlucoseValueDao : TraceableDao { @Query("DELETE FROM $TABLE_GLUCOSE_VALUES") override fun deleteAllEntries() - @Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE isValid = 1 AND referenceId IS NULL ORDER BY id DESC limit 1") + @Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE isValid = 1 AND referenceId IS NULL ORDER BY timestamp DESC limit 1") fun getLast(): Maybe @Query("SELECT id FROM $TABLE_GLUCOSE_VALUES ORDER BY id DESC limit 1") From e129d10b419d19c8285b968d3688bc05beb51cba Mon Sep 17 00:00:00 2001 From: Selcuk Kekec Date: Sat, 20 May 2023 08:47:38 -0600 Subject: [PATCH 4/7] Smoothing migrated to master branch --- .../androidaps/dialogs/CarbsDialog.kt | 3 +- .../androidaps/dialogs/WizardDialog.kt | 2 +- .../plugins/general/autotune/data/BGDatum.kt | 2 +- .../plugins/general/overview/OverviewData.kt | 4 +- .../general/overview/OverviewFragment.kt | 2 +- .../graphExtensions/GlucoseValueDataPoint.kt | 9 +- .../InMemoryGlucoseValueDataPoint.kt | 7 +- .../PersistentNotificationPlugin.kt | 4 +- .../smsCommunicator/SmsCommunicatorPlugin.kt | 4 +- .../wear/wearintegration/DataHandlerMobile.kt | 6 +- .../IobCobCalculatorPlugin.kt | 2 +- .../iob/iobCobCalculator/IobCobOref1Worker.kt | 8 +- .../iob/iobCobCalculator/IobCobOrefWorker.kt | 8 +- .../androidaps/plugins/source/AidexPlugin.kt | 1 + .../plugins/source/BGSourceFragment.kt | 6 +- .../androidaps/plugins/source/DexcomPlugin.kt | 3 +- .../plugins/source/EversensePlugin.kt | 1 + .../androidaps/plugins/source/GlimpPlugin.kt | 1 + .../plugins/source/GlunovoPlugin.kt | 1 + .../androidaps/plugins/source/MM640gPlugin.kt | 1 + .../plugins/source/NSClientSourcePlugin.kt | 3 +- .../plugins/source/PoctechPlugin.kt | 1 + .../plugins/source/RandomBgPlugin.kt | 1 + .../androidaps/plugins/source/TomatoPlugin.kt | 1 + .../androidaps/plugins/source/XdripPlugin.kt | 1 + .../androidaps/utils/TrendCalculator.kt | 7 +- .../utils/wizard/QuickWizardEntry.kt | 2 +- .../nightscout/androidaps/widget/Widget.kt | 2 +- .../workflow/PrepareBgDataWorker.kt | 9 +- .../workflow/PrepareBucketedDataWorker.kt | 4 +- .../workflow/PreparePredictionsWorker.kt | 4 +- .../workflow/PrepareTreatmentsDataWorker.kt | 7 +- app/src/main/res/values/strings.xml | 7 +- app/src/main/res/xml/pref_overview.xml | 6 + .../SmsCommunicatorPluginTest.kt | 3 +- .../androidaps/data/InMemoryGlucoseValue.kt | 14 +- .../extensions/GlucoseValueExtension.kt | 28 +- .../androidaps/plugins/aps/loop/APSResult.kt | 5 + .../iob/iobCobCalculator/AutosensDataStore.kt | 35 +- .../iobCobCalculator/GlucoseStatusProvider.kt | 16 +- core/src/main/res/values/strings.xml | 1 + .../22.json | 3599 +++++++++++++++++ .../androidaps/database/AppDatabase.kt | 2 +- .../androidaps/database/DatabaseModule.kt | 28 + .../database/entities/GlucoseValue.kt | 1 + .../transactions/CgmSourceTransaction.kt | 130 +- 46 files changed, 3923 insertions(+), 69 deletions(-) create mode 100644 database/schemas/info.nightscout.androidaps.database.AppDatabase/22.json diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt index ff3d0c778a..b4250d51bb 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt @@ -19,6 +19,7 @@ import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction import info.nightscout.androidaps.databinding.DialogCarbsBinding import info.nightscout.androidaps.extensions.formatColor +import info.nightscout.androidaps.extensions.rawOrSmoothed import info.nightscout.androidaps.interfaces.* import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger @@ -184,7 +185,7 @@ class CarbsDialog : DialogFragmentWithDate() { } iobCobCalculator.ads.actualBg()?.let { bgReading -> - if (bgReading.value < 72) + if (bgReading.rawOrSmoothed(sp) < 72) binding.hypoTt.isChecked = true } binding.hypoTt.setOnClickListener { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt index 9c8fe4ba91..e280da9561 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt @@ -365,7 +365,7 @@ class WizardDialog : DaggerDialogFragment() { binding.bgInput.step = if (units == GlucoseUnit.MGDL) 1.0 else 0.1 // Set BG if not old - binding.bgInput.value = iobCobCalculator.ads.actualBg()?.valueToUnits(units) ?: 0.0 + binding.bgInput.value = iobCobCalculator.ads.actualBg()?.valueToUnits(units, sp) ?: 0.0 binding.ttCheckbox.isEnabled = tempTarget is ValueWrapper.Existing binding.ttCheckboxIcon.visibility = binding.ttCheckbox.isEnabled.toVisibility() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/data/BGDatum.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/data/BGDatum.kt index 7d2f7b6f8c..2d019a410a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/data/BGDatum.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/autotune/data/BGDatum.kt @@ -31,7 +31,7 @@ class BGDatum { constructor(json: JSONObject, dateUtil: DateUtil) { this.dateUtil = dateUtil try { - //if (json.has("_id")) id = json.getLong("_id") + if (json.has("_id")) id = json.getLong("_id") if (json.has("date")) date = json.getLong("date") if (json.has("sgv")) value = json.getDouble("sgv") if (json.has("direction")) direction = TrendArrow.fromString(json.getString("direction")) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt index 6f0b64bc6d..6be9c34aef 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt @@ -124,12 +124,12 @@ class OverviewData @Inject constructor( val isLow: Boolean get() = lastBg?.let { lastBg -> - lastBg.valueToUnits(profileFunction.getUnits()) < defaultValueHelper.determineLowLine() + lastBg.valueToUnits(profileFunction.getUnits(), sp) < defaultValueHelper.determineLowLine() } ?: false val isHigh: Boolean get() = lastBg?.let { lastBg -> - lastBg.valueToUnits(profileFunction.getUnits()) > defaultValueHelper.determineHighLine() + lastBg.valueToUnits(profileFunction.getUnits(), sp) > defaultValueHelper.determineHighLine() } ?: false @ColorInt diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt index c3bc8aa091..236b03f49a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt @@ -788,7 +788,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList val lastBgDescription = overviewData.lastBgDescription runOnUiThread { _binding ?: return@runOnUiThread - binding.infoLayout.bg.text = lastBg?.valueToUnitsString(units) + binding.infoLayout.bg.text = lastBg?.valueToUnitsString(units, sp) ?: rh.gs(R.string.notavailable) binding.infoLayout.bg.setTextColor(lastBgColor) binding.infoLayout.arrow.setImageResource(trendArrow.directionToIcon()) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/GlucoseValueDataPoint.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/GlucoseValueDataPoint.kt index af527dec05..d0d4d3862f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/GlucoseValueDataPoint.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/GlucoseValueDataPoint.kt @@ -4,27 +4,30 @@ import android.content.Context import info.nightscout.androidaps.Constants import info.nightscout.androidaps.core.R import info.nightscout.androidaps.database.entities.GlucoseValue +import info.nightscout.androidaps.extensions.rawOrSmoothed import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.utils.DefaultValueHelper +import info.nightscout.shared.sharedPreferences.SP class GlucoseValueDataPoint( val data: GlucoseValue, private val defaultValueHelper: DefaultValueHelper, private val profileFunction: ProfileFunction, - private val rh: ResourceHelper + private val rh: ResourceHelper, + private val sp: SP ) : DataPointWithLabelInterface { fun valueToUnits(units: GlucoseUnit): Double = - if (units == GlucoseUnit.MGDL) data.value else data.value * Constants.MGDL_TO_MMOLL + if (units == GlucoseUnit.MGDL) data.rawOrSmoothed(sp) else data.rawOrSmoothed(sp) * Constants.MGDL_TO_MMOLL override fun getX(): Double = data.timestamp.toDouble() override fun getY(): Double = valueToUnits(profileFunction.getUnits()) override fun setY(y: Double) {} - override val label: String = Profile.toCurrentUnitsString(profileFunction, data.value) + override val label: String = Profile.toCurrentUnitsString(profileFunction, data.rawOrSmoothed(sp)) override val duration = 0L override val shape get() = if (isPrediction) PointsWithLabelGraphSeries.Shape.PREDICTION else PointsWithLabelGraphSeries.Shape.BG override val size = 1f diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/InMemoryGlucoseValueDataPoint.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/InMemoryGlucoseValueDataPoint.kt index 712ef22895..1dac43f495 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/InMemoryGlucoseValueDataPoint.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/InMemoryGlucoseValueDataPoint.kt @@ -7,15 +7,18 @@ import info.nightscout.androidaps.data.InMemoryGlucoseValue import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.shared.sharedPreferences.SP class InMemoryGlucoseValueDataPoint( val data: InMemoryGlucoseValue, private val profileFunction: ProfileFunction, - private val rh: ResourceHelper + private val rh: ResourceHelper, + private val sp: SP ) : DataPointWithLabelInterface { fun valueToUnits(units: GlucoseUnit): Double = - if (units == GlucoseUnit.MGDL) data.value else data.value * Constants.MGDL_TO_MMOLL + if (units == GlucoseUnit.MGDL) data.rawOrSmoothed(sp) + else data.rawOrSmoothed(sp) * Constants.MGDL_TO_MMOLL override fun getX(): Double = data.timestamp.toDouble() override fun getY(): Double = valueToUnits(profileFunction.getUnits()) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt index 0a4072250b..8a7d7850ab 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt @@ -23,6 +23,7 @@ import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.sharedPreferences.SP import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject @@ -34,6 +35,7 @@ class PersistentNotificationPlugin @Inject constructor( injector: HasAndroidInjector, aapsLogger: AAPSLogger, rh: ResourceHelper, + private val sp: SP, private val aapsSchedulers: AapsSchedulers, private val profileFunction: ProfileFunction, private val fabricPrivacy: FabricPrivacy, @@ -112,7 +114,7 @@ class PersistentNotificationPlugin @Inject constructor( val lastBG = iobCobCalculator.ads.lastBg() val glucoseStatus = glucoseStatusProvider.glucoseStatusData if (lastBG != null) { - line1aa = lastBG.valueToUnitsString(units) + line1aa = lastBG.valueToUnitsString(units, sp) line1 = line1aa if (glucoseStatus != null) { line1 += (" Δ" + Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index 311b1999fa..dac28baff9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -324,11 +324,11 @@ class SmsCommunicatorPlugin @Inject constructor( var reply = "" val units = profileFunction.getUnits() if (actualBG != null) { - reply = rh.gs(R.string.sms_actualbg) + " " + actualBG.valueToUnitsString(units) + ", " + reply = rh.gs(R.string.sms_actualbg) + " " + actualBG.valueToUnitsString(units, sp) + ", " } else if (lastBG != null) { val agoMilliseconds = dateUtil.now() - lastBG.timestamp val agoMin = (agoMilliseconds / 60.0 / 1000.0).toInt() - reply = rh.gs(R.string.sms_lastbg) + " " + lastBG.valueToUnitsString(units) + " " + rh.gs(R.string.sms_minago, agoMin) + ", " + reply = rh.gs(R.string.sms_lastbg) + " " + lastBG.valueToUnitsString(units, sp) + " " + rh.gs(R.string.sms_minago, agoMin) + ", " } val glucoseStatus = glucoseStatusProvider.glucoseStatusData if (glucoseStatus != null) reply += rh.gs(R.string.sms_delta) + " " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units + ", " diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt index 64623dbf12..fe7b5f8e26 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt @@ -366,7 +366,7 @@ class DataHandlerMobile @Inject constructor( tempTarget = tempTarget, carbs = carbsAfterConstraints, cob = cobInfo.displayCob!!, - bg = bgReading.valueToUnits(profileFunction.getUnits()), + bg = bgReading.valueToUnits(profileFunction.getUnits(), sp), correction = 0.0, percentageCorrection = percentage, useBg = sp.getBoolean(R.string.key_wearwizard_bg, true), @@ -830,7 +830,7 @@ class DataHandlerMobile @Inject constructor( val finalLastRun = loop.lastRun if (sp.getBoolean("wear_predictions", true) && finalLastRun?.request?.hasPredictions == true && finalLastRun.constraintsProcessed != null) { val predArray = finalLastRun.constraintsProcessed!!.predictions - .stream().map { bg: GlucoseValue -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh) } + .stream().map { bg: GlucoseValue -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh, sp) } .collect(Collectors.toList()) if (predArray.isNotEmpty()) for (bg in predArray) if (bg.data.value > 39) @@ -917,7 +917,7 @@ class DataHandlerMobile @Inject constructor( return EventData.SingleBg( timeStamp = glucoseValue.timestamp, - sgvString = glucoseValue.valueToUnitsString(units), + sgvString = glucoseValue.valueToUnitsString(units, sp), glucoseUnits = units.asText, slopeArrow = trendCalculator.getTrendArrow(glucoseValue).symbol, delta = glucoseStatus?.let { deltaString(it.delta, it.delta * Constants.MGDL_TO_MMOLL, units) } ?: "--", diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt index 30c05b1f32..c66ea28e58 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt @@ -77,7 +77,7 @@ class IobCobCalculatorPlugin @Inject constructor( private var iobTable = LongSparseArray() // oldest at index 0 private var basalDataTable = LongSparseArray() // oldest at index 0 - override var ads: AutosensDataStore = AutosensDataStore() + override var ads: AutosensDataStore = AutosensDataStore(sp) private val dataLock = Any() private var thread: Thread? = null diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOref1Worker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOref1Worker.kt index 12f240b1e3..2535f242ef 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOref1Worker.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOref1Worker.kt @@ -133,14 +133,14 @@ class IobCobOref1Worker( //console.error(bgTime , bucketed_data[i].glucose); var avgDelta: Double var delta: Double - val bg: Double = bucketedData[i].value - if (bg < 39 || bucketedData[i + 3].value < 39) { + val bg: Double = bucketedData[i].rawOrSmoothed(sp) + if (bg < 39 || bucketedData[i + 3].rawOrSmoothed(sp) < 39) { aapsLogger.error("! value < 39") continue } autosensData.bg = bg - delta = bg - bucketedData[i + 1].value - avgDelta = (bg - bucketedData[i + 3].value) / 3 + delta = bg - bucketedData[i + 1].rawOrSmoothed(sp) + avgDelta = (bg - bucketedData[i + 3].rawOrSmoothed(sp)) / 3 val iob = data.iobCobCalculator.calculateFromTreatmentsAndTemps(bgTime, profile) val bgi = -iob.activity * sens * 5 val deviation = delta - bgi diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOrefWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOrefWorker.kt index 347d4bb573..dc12efa046 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOrefWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOrefWorker.kt @@ -128,14 +128,14 @@ class IobCobOrefWorker @Inject internal constructor( //console.error(bgTime , bucketed_data[i].glucose); var avgDelta: Double var delta: Double - val bg: Double = bucketedData[i].value - if (bg < 39 || bucketedData[i + 3].value < 39) { + val bg: Double = bucketedData[i].rawOrSmoothed(sp) + if (bg < 39 || bucketedData[i + 3].rawOrSmoothed(sp) < 39) { aapsLogger.error("! value < 39") continue } autosensData.bg = bg - delta = bg - bucketedData[i + 1].value - avgDelta = (bg - bucketedData[i + 3].value) / 3 + delta = bg - bucketedData[i + 1].rawOrSmoothed(sp) + avgDelta = (bg - bucketedData[i + 3].rawOrSmoothed(sp)) / 3 val iob = data.iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile) val bgi = -iob.activity * sens * 5 val deviation = delta - bgi diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/AidexPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/AidexPlugin.kt index 3e0a9d2d8c..adebd91690 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/AidexPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/AidexPlugin.kt @@ -93,6 +93,7 @@ class AidexPlugin @Inject constructor( glucoseValues += CgmSourceTransaction.TransactionGlucoseValue( timestamp = timestamp, value = bgValueTarget, + smoothed = null, raw = null, noise = null, trendArrow = GlucoseValue.TrendArrow.fromString(bundle.getString(Intents.AIDEX_BG_SLOPE_NAME)), diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt index a3fd240570..0c7a489641 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt @@ -34,6 +34,7 @@ import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag +import info.nightscout.shared.sharedPreferences.SP import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import java.util.concurrent.TimeUnit @@ -51,6 +52,7 @@ class BGSourceFragment : DaggerFragment() { @Inject lateinit var uel: UserEntryLogger @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var sp: SP private val disposable = CompositeDisposable() private val millsToThePast = T.hours(36).msecs() @@ -139,7 +141,7 @@ class BGSourceFragment : DaggerFragment() { holder.binding.date.visibility = newDay.toVisibility() holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(glucoseValue.timestamp, rh) else "" holder.binding.time.text = dateUtil.timeString(glucoseValue.timestamp) - holder.binding.value.text = glucoseValue.valueToUnitsString(profileFunction.getUnits()) + holder.binding.value.text = glucoseValue.valueToUnitsString(profileFunction.getUnits(), sp) holder.binding.direction.setImageResource(glucoseValue.trendArrow.directionToIcon()) if (position > 0) { val previous = glucoseValues[position - 1] @@ -180,7 +182,7 @@ class BGSourceFragment : DaggerFragment() { private fun getConfirmationText(selectedItems: SparseArray): String { if (selectedItems.size() == 1) { val glucoseValue = selectedItems.valueAt(0) - return dateUtil.dateAndTimeString(glucoseValue.timestamp) + "\n" + glucoseValue.valueToUnitsString(profileFunction.getUnits()) + return dateUtil.dateAndTimeString(glucoseValue.timestamp) + "\n" + glucoseValue.valueToUnitsString(profileFunction.getUnits(), sp) } return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/DexcomPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/DexcomPlugin.kt index a2f84ab42d..4709bf17b0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/DexcomPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/DexcomPlugin.kt @@ -143,8 +143,9 @@ class DexcomPlugin @Inject constructor( glucoseValues += CgmSourceTransaction.TransactionGlucoseValue( timestamp = timestamp, value = glucoseValueBundle.getInt("glucoseValue").toDouble(), - noise = null, raw = null, + smoothed = null, + noise = null, trendArrow = GlucoseValue.TrendArrow.fromString(glucoseValueBundle.getString("trendArrow")!!), sourceSensor = sourceSensor ) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/EversensePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/EversensePlugin.kt index 05715e8e12..45d9d26b03 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/EversensePlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/EversensePlugin.kt @@ -105,6 +105,7 @@ class EversensePlugin @Inject constructor( timestamp = glucoseTimestamps[i], value = glucoseLevels[i].toDouble(), raw = glucoseLevels[i].toDouble(), + smoothed = null, noise = null, trendArrow = GlucoseValue.TrendArrow.NONE, sourceSensor = GlucoseValue.SourceSensor.EVERSENSE diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/GlimpPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/GlimpPlugin.kt index 8efc9651df..defe9b72db 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/GlimpPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/GlimpPlugin.kt @@ -65,6 +65,7 @@ class GlimpPlugin @Inject constructor( timestamp = inputData.getLong("myTimestamp", 0), value = inputData.getDouble("mySGV", 0.0), raw = inputData.getDouble("mySGV", 0.0), + smoothed = null, noise = null, trendArrow = GlucoseValue.TrendArrow.fromString(inputData.getString("myTrend")), sourceSensor = GlucoseValue.SourceSensor.GLIMP diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/GlunovoPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/GlunovoPlugin.kt index 6a02daa322..98e225b165 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/GlunovoPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/GlunovoPlugin.kt @@ -123,6 +123,7 @@ class GlunovoPlugin @Inject constructor( timestamp = timestamp, value = value * Constants.MMOLL_TO_MGDL, raw = 0.0, + smoothed = null, noise = null, trendArrow = GlucoseValue.TrendArrow.NONE, sourceSensor = GlucoseValue.SourceSensor.GLUNOVO_NATIVE diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/MM640gPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/MM640gPlugin.kt index dd70a31e99..891617a937 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/MM640gPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/MM640gPlugin.kt @@ -78,6 +78,7 @@ class MM640gPlugin @Inject constructor( timestamp = jsonObject.getLong("date"), value = jsonObject.getDouble("sgv"), raw = jsonObject.getDouble("sgv"), + smoothed = null, noise = null, trendArrow = GlucoseValue.TrendArrow.fromString(jsonObject.getString("direction")), sourceSensor = GlucoseValue.SourceSensor.MM_600_SERIES diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/NSClientSourcePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/NSClientSourcePlugin.kt index 6ada149c6d..bb0b0dbc1b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/NSClientSourcePlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/NSClientSourcePlugin.kt @@ -104,8 +104,9 @@ class NSClientSourcePlugin @Inject constructor( return CgmSourceTransaction.TransactionGlucoseValue( timestamp = sgv.mills ?: return null, value = sgv.mgdl?.toDouble() ?: return null, - noise = null, raw = sgv.filtered?.toDouble() ?: sgv.mgdl?.toDouble(), + smoothed = null, + noise = null, trendArrow = GlucoseValue.TrendArrow.fromString(sgv.direction), nightscoutId = sgv.id, sourceSensor = GlucoseValue.SourceSensor.fromString(sgv.device) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/PoctechPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/PoctechPlugin.kt index 072d1953bc..7a2c6e7a0d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/PoctechPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/PoctechPlugin.kt @@ -72,6 +72,7 @@ class PoctechPlugin @Inject constructor( timestamp = json.getLong("date"), value = if (safeGetString(json, "units", Constants.MGDL) == "mmol/L") json.getDouble("current") * Constants.MMOLL_TO_MGDL else json.getDouble("current"), + smoothed = null, raw = json.getDouble("raw"), noise = null, trendArrow = GlucoseValue.TrendArrow.fromString(json.getString("direction")), diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt index c2115d53e3..5fc097e6cb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt @@ -109,6 +109,7 @@ class RandomBgPlugin @Inject constructor( value = bgMgdl, raw = 0.0, noise = null, + smoothed = null, trendArrow = GlucoseValue.TrendArrow.NONE, sourceSensor = GlucoseValue.SourceSensor.RANDOM ) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/TomatoPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/TomatoPlugin.kt index da48d8f46a..61fa45d697 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/TomatoPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/TomatoPlugin.kt @@ -66,6 +66,7 @@ class TomatoPlugin @Inject constructor( value = inputData.getDouble("com.fanqies.tomatofn.Extras.BgEstimate", 0.0), raw = 0.0, noise = null, + smoothed = null, trendArrow = GlucoseValue.TrendArrow.NONE, sourceSensor = GlucoseValue.SourceSensor.LIBRE_1_TOMATO ) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/XdripPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/XdripPlugin.kt index 906e84cf36..fead129554 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/XdripPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/XdripPlugin.kt @@ -83,6 +83,7 @@ class XdripPlugin @Inject constructor( timestamp = bundle.getLong(Intents.EXTRA_TIMESTAMP, 0), value = bundle.getDouble(Intents.EXTRA_BG_ESTIMATE, 0.0), raw = bundle.getDouble(Intents.EXTRA_RAW, 0.0), + smoothed = null, noise = null, trendArrow = GlucoseValue.TrendArrow.fromString(bundle.getString(Intents.EXTRA_BG_SLOPE_NAME)), sourceSensor = GlucoseValue.SourceSensor.fromString(bundle.getString(Intents.XDRIP_DATA_SOURCE_DESCRIPTION) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/TrendCalculator.kt b/app/src/main/java/info/nightscout/androidaps/utils/TrendCalculator.kt index 2d161808a6..6b188c3e3f 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/TrendCalculator.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/TrendCalculator.kt @@ -4,14 +4,17 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.entities.GlucoseValue.TrendArrow.* +import info.nightscout.androidaps.extensions.rawOrSmoothed import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.shared.sharedPreferences.SP import javax.inject.Inject import javax.inject.Singleton @Singleton class TrendCalculator @Inject constructor( private val repository: AppRepository, - private val rh: ResourceHelper + private val rh: ResourceHelper, + private val sp: SP ) { fun getTrendArrow(glucoseValue: GlucoseValue?): GlucoseValue.TrendArrow = @@ -47,7 +50,7 @@ class TrendCalculator @Inject constructor( // Avoid division by 0 val slope = if (current.timestamp == previous.timestamp) 0.0 - else (previous.value - current.value) / (previous.timestamp - current.timestamp) + else (previous.rawOrSmoothed(sp) - current.rawOrSmoothed(sp)) / (previous.timestamp - current.timestamp) val slopeByMinute = slope * 60000 diff --git a/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizardEntry.kt b/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizardEntry.kt index c6dee8a660..ffe959f32f 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizardEntry.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizardEntry.kt @@ -103,7 +103,7 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec //BG var bg = 0.0 if (useBG() == YES) { - bg = lastBG.valueToUnits(profileFunction.getUnits()) + bg = lastBG.valueToUnits(profileFunction.getUnits(), sp) } // COB val cob = diff --git a/app/src/main/java/info/nightscout/androidaps/widget/Widget.kt b/app/src/main/java/info/nightscout/androidaps/widget/Widget.kt index caf2bc46f4..f2e08d0425 100644 --- a/app/src/main/java/info/nightscout/androidaps/widget/Widget.kt +++ b/app/src/main/java/info/nightscout/androidaps/widget/Widget.kt @@ -105,7 +105,7 @@ class Widget : AppWidgetProvider() { private fun updateBg(views: RemoteViews) { val units = profileFunction.getUnits() - views.setTextViewText(R.id.bg, overviewData.lastBg?.valueToUnitsString(units) ?: rh.gs(R.string.notavailable)) + views.setTextViewText(R.id.bg, overviewData.lastBg?.valueToUnitsString(units, sp) ?: rh.gs(R.string.notavailable)) views.setTextColor( R.id.bg, when { overviewData.isLow -> rh.gc(R.color.widget_low) diff --git a/app/src/main/java/info/nightscout/androidaps/workflow/PrepareBgDataWorker.kt b/app/src/main/java/info/nightscout/androidaps/workflow/PrepareBgDataWorker.kt index 16ad858693..d0fa6ec781 100644 --- a/app/src/main/java/info/nightscout/androidaps/workflow/PrepareBgDataWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/workflow/PrepareBgDataWorker.kt @@ -5,7 +5,9 @@ import androidx.work.Worker import androidx.work.WorkerParameters import androidx.work.workDataOf import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.extensions.rawOrSmoothed import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.Profile @@ -18,6 +20,7 @@ import info.nightscout.androidaps.receivers.DataWorker import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.Round import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.shared.sharedPreferences.SP import java.util.ArrayList import javax.inject.Inject @@ -29,6 +32,7 @@ class PrepareBgDataWorker( @Inject lateinit var dataWorker: DataWorker @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var rh: ResourceHelper + @Inject lateinit var sp: SP @Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var repository: AppRepository @@ -45,14 +49,15 @@ class PrepareBgDataWorker( val data = dataWorker.pickupObject(inputData.getLong(DataWorker.STORE_KEY, -1)) as PrepareBgData? ?: return Result.failure(workDataOf("Error" to "missing input data")) + val useSmoothed = sp.getBoolean(R.string.key_use_data_smoothing, false) data.overviewData.maxBgValue = Double.MIN_VALUE data.overviewData.bgReadingsArray = repository.compatGetBgReadingsDataFromTime(data.overviewData.fromTime, data.overviewData.toTime, false).blockingGet() val bgListArray: MutableList = ArrayList() for (bg in data.overviewData.bgReadingsArray) { if (bg.timestamp < data.overviewData.fromTime || bg.timestamp > data.overviewData.toTime) continue - if (bg.value > data.overviewData.maxBgValue) data.overviewData.maxBgValue = bg.value - bgListArray.add(GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh)) + if (bg.rawOrSmoothed(useSmoothed) > data.overviewData.maxBgValue) data.overviewData.maxBgValue = bg.rawOrSmoothed(useSmoothed) + bgListArray.add(GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh, sp)) } bgListArray.sortWith { o1: DataPointWithLabelInterface, o2: DataPointWithLabelInterface -> o1.x.compareTo(o2.x) } data.overviewData.bgReadingGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] }) diff --git a/app/src/main/java/info/nightscout/androidaps/workflow/PrepareBucketedDataWorker.kt b/app/src/main/java/info/nightscout/androidaps/workflow/PrepareBucketedDataWorker.kt index db2d6af09d..14efd0476f 100644 --- a/app/src/main/java/info/nightscout/androidaps/workflow/PrepareBucketedDataWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/workflow/PrepareBucketedDataWorker.kt @@ -14,6 +14,7 @@ import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Point import info.nightscout.androidaps.receivers.DataWorker import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.sharedPreferences.SP import javax.inject.Inject class PrepareBucketedDataWorker( @@ -25,6 +26,7 @@ class PrepareBucketedDataWorker( @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var rh: ResourceHelper @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var sp: SP init { (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) @@ -48,7 +50,7 @@ class PrepareBucketedDataWorker( val bucketedListArray: MutableList = ArrayList() for (inMemoryGlucoseValue in bucketedData) { if (inMemoryGlucoseValue.timestamp < data.overviewData.fromTime || inMemoryGlucoseValue.timestamp > data.overviewData.toTime) continue - bucketedListArray.add(InMemoryGlucoseValueDataPoint(inMemoryGlucoseValue, profileFunction, rh)) + bucketedListArray.add(InMemoryGlucoseValueDataPoint(inMemoryGlucoseValue, profileFunction, rh, sp)) } bucketedListArray.sortWith { o1: DataPointWithLabelInterface, o2: DataPointWithLabelInterface -> o1.x.compareTo(o2.x) } data.overviewData.bucketedGraphSeries = PointsWithLabelGraphSeries(Array(bucketedListArray.size) { i -> bucketedListArray[i] }) diff --git a/app/src/main/java/info/nightscout/androidaps/workflow/PreparePredictionsWorker.kt b/app/src/main/java/info/nightscout/androidaps/workflow/PreparePredictionsWorker.kt index 0e94ce025a..36e5b608dc 100644 --- a/app/src/main/java/info/nightscout/androidaps/workflow/PreparePredictionsWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/workflow/PreparePredictionsWorker.kt @@ -20,6 +20,7 @@ import info.nightscout.androidaps.receivers.DataWorker import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.shared.sharedPreferences.SP import java.util.* import javax.inject.Inject import kotlin.math.ceil @@ -43,6 +44,7 @@ class PreparePredictionsWorker( @Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var rh: ResourceHelper + @Inject lateinit var sp: SP init { (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) @@ -83,7 +85,7 @@ class PreparePredictionsWorker( val bgListArray: MutableList = ArrayList() val predictions: MutableList? = apsResult?.predictions - ?.map { bg -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh) } + ?.map { bg -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh, sp) } ?.toMutableList() if (predictions != null) { predictions.sortWith { o1: GlucoseValueDataPoint, o2: GlucoseValueDataPoint -> o1.x.compareTo(o2.x) } diff --git a/app/src/main/java/info/nightscout/androidaps/workflow/PrepareTreatmentsDataWorker.kt b/app/src/main/java/info/nightscout/androidaps/workflow/PrepareTreatmentsDataWorker.kt index 2278413459..277dd2e27d 100644 --- a/app/src/main/java/info/nightscout/androidaps/workflow/PrepareTreatmentsDataWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/workflow/PrepareTreatmentsDataWorker.kt @@ -8,6 +8,7 @@ import dagger.android.HasAndroidInjector import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.Bolus import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.extensions.rawOrSmoothed import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.Profile @@ -22,6 +23,7 @@ import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.Round import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.Translator +import info.nightscout.shared.sharedPreferences.SP import javax.inject.Inject class PrepareTreatmentsDataWorker( @@ -37,6 +39,7 @@ class PrepareTreatmentsDataWorker( @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var repository: AppRepository @Inject lateinit var defaultValueHelper: DefaultValueHelper + @Inject lateinit var sp: SP init { (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) @@ -137,9 +140,9 @@ class PrepareTreatmentsDataWorker( overviewData.bgReadingsArray.let { bgReadingsArray -> for (reading in bgReadingsArray) { if (reading.timestamp > date) continue - return Profile.fromMgdlToUnits(reading.value, profileFunction.getUnits()) + return Profile.fromMgdlToUnits(reading.rawOrSmoothed(sp), profileFunction.getUnits()) } - return if (bgReadingsArray.isNotEmpty()) Profile.fromMgdlToUnits(bgReadingsArray[0].value, profileFunction.getUnits()) + return if (bgReadingsArray.isNotEmpty()) Profile.fromMgdlToUnits(bgReadingsArray[0].rawOrSmoothed(sp), profileFunction.getUnits()) else Profile.fromMgdlToUnits(100.0, profileFunction.getUnits()) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e8bd49c198..999c84be37 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1302,5 +1302,10 @@ EN Variant key_en_max_iob key_en_max_iob_allow_smb - + + use_data_smoothing + Smooth and display incoming sensor data in the main chart. Recommended if your glucose source does not smooth sensor readings by itself and you would like to enable SMB always. + Smooth incoming glucose values + Smoothing of incoming sensor readings is enabled. + diff --git a/app/src/main/res/xml/pref_overview.xml b/app/src/main/res/xml/pref_overview.xml index d994207571..bbd8810675 100644 --- a/app/src/main/res/xml/pref_overview.xml +++ b/app/src/main/res/xml/pref_overview.xml @@ -14,6 +14,12 @@ android:summary="@string/keep_screen_on_summary" android:title="@string/keep_screen_on_title" /> + + diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.kt b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.kt index 55df56b198..52528d98fd 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.kt @@ -82,7 +82,8 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { private var hasBeenRun = false @Before fun prepareTests() { - val reading = GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = 1514766900000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT) + val reading = GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, smoothed = null, timestamp = 1514766900000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue + .TrendArrow.FLAT) val bgList: MutableList = ArrayList() bgList.add(reading) diff --git a/core/src/main/java/info/nightscout/androidaps/data/InMemoryGlucoseValue.kt b/core/src/main/java/info/nightscout/androidaps/data/InMemoryGlucoseValue.kt index 00a89d48db..1fd360a5c1 100644 --- a/core/src/main/java/info/nightscout/androidaps/data/InMemoryGlucoseValue.kt +++ b/core/src/main/java/info/nightscout/androidaps/data/InMemoryGlucoseValue.kt @@ -1,9 +1,19 @@ package info.nightscout.androidaps.data +import info.nightscout.androidaps.core.R import info.nightscout.androidaps.database.entities.GlucoseValue +import info.nightscout.shared.sharedPreferences.SP -class InMemoryGlucoseValue constructor(var timestamp: Long = 0L, var value: Double = 0.0, var interpolated: Boolean = false) { +class InMemoryGlucoseValue constructor(var timestamp: Long = 0L, var value: Double = 0.0, var smoothed: Double? = null, var interpolated: Boolean = false) { - constructor(gv: GlucoseValue) : this(gv.timestamp, gv.value) + constructor(gv: GlucoseValue) : this(gv.timestamp, gv.value, gv.smoothed) // var generated : value doesn't correspond to real value with timestamp close to real BG + private fun useDataSmoothing(sp: SP): Boolean { + return sp.getBoolean(R.string.key_use_data_smoothing, false) + } + + fun rawOrSmoothed(sp: SP): Double { + if (useDataSmoothing(sp)) return smoothed ?: value + else return value + } } \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/GlucoseValueExtension.kt b/core/src/main/java/info/nightscout/androidaps/extensions/GlucoseValueExtension.kt index a74fdff40b..06aac714af 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/GlucoseValueExtension.kt +++ b/core/src/main/java/info/nightscout/androidaps/extensions/GlucoseValueExtension.kt @@ -1,19 +1,35 @@ package info.nightscout.androidaps.extensions import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.core.R import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DecimalFormatter +import info.nightscout.shared.sharedPreferences.SP import org.json.JSONObject -fun GlucoseValue.valueToUnits(units: GlucoseUnit): Double = - if (units == GlucoseUnit.MGDL) value - else value * Constants.MGDL_TO_MMOLL +fun useDataSmoothing(sp: SP): Boolean { + return sp.getBoolean(R.string.key_use_data_smoothing, false) +} -fun GlucoseValue.valueToUnitsString(units: GlucoseUnit): String = - if (units == GlucoseUnit.MGDL) DecimalFormatter.to0Decimal(value) - else DecimalFormatter.to1Decimal(value * Constants.MGDL_TO_MMOLL) +fun GlucoseValue.rawOrSmoothed(sp: SP): Double { + if (useDataSmoothing(sp)) return smoothed ?: value + else return value +} + +fun GlucoseValue.rawOrSmoothed(useSmoothed: Boolean): Double { + if (useSmoothed) return smoothed ?: value + else return value +} + +fun GlucoseValue.valueToUnits(units: GlucoseUnit, sp: SP): Double = + if (units == GlucoseUnit.MGDL) rawOrSmoothed(sp) + else rawOrSmoothed(sp) * Constants.MGDL_TO_MMOLL + +fun GlucoseValue.valueToUnitsString(units: GlucoseUnit, sp: SP): String = + if (units == GlucoseUnit.MGDL) DecimalFormatter.to0Decimal(rawOrSmoothed(sp)) + else DecimalFormatter.to1Decimal(rawOrSmoothed(sp) * Constants.MGDL_TO_MMOLL) fun GlucoseValue.toJson(isAdd : Boolean, dateUtil: DateUtil): JSONObject = JSONObject() diff --git a/core/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.kt b/core/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.kt index c96b0b405b..c4216896fd 100644 --- a/core/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.kt +++ b/core/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.kt @@ -199,6 +199,7 @@ open class APSResult @Inject constructor(val injector: HasAndroidInjector) { raw = 0.0, noise = 0.0, value = iob.getInt(i).toDouble(), + smoothed = iob.getInt(i).toDouble(), timestamp = startTime + i * 5 * 60 * 1000L, sourceSensor = GlucoseValue.SourceSensor.IOB_PREDICTION, trendArrow = GlucoseValue.TrendArrow.NONE @@ -213,6 +214,7 @@ open class APSResult @Inject constructor(val injector: HasAndroidInjector) { raw = 0.0, noise = 0.0, value = iob.getInt(i).toDouble(), + smoothed = iob.getInt(i).toDouble(), timestamp = startTime + i * 5 * 60 * 1000L, sourceSensor = GlucoseValue.SourceSensor.A_COB_PREDICTION, trendArrow = GlucoseValue.TrendArrow.NONE @@ -227,6 +229,7 @@ open class APSResult @Inject constructor(val injector: HasAndroidInjector) { raw = 0.0, noise = 0.0, value = iob.getInt(i).toDouble(), + smoothed = iob.getInt(i).toDouble(), timestamp = startTime + i * 5 * 60 * 1000L, sourceSensor = GlucoseValue.SourceSensor.COB_PREDICTION, trendArrow = GlucoseValue.TrendArrow.NONE @@ -241,6 +244,7 @@ open class APSResult @Inject constructor(val injector: HasAndroidInjector) { raw = 0.0, noise = 0.0, value = iob.getInt(i).toDouble(), + smoothed = iob.getInt(i).toDouble(), timestamp = startTime + i * 5 * 60 * 1000L, sourceSensor = GlucoseValue.SourceSensor.UAM_PREDICTION, trendArrow = GlucoseValue.TrendArrow.NONE @@ -255,6 +259,7 @@ open class APSResult @Inject constructor(val injector: HasAndroidInjector) { raw = 0.0, noise = 0.0, value = iob.getInt(i).toDouble(), + smoothed = iob.getInt(i).toDouble(), timestamp = startTime + i * 5 * 60 * 1000L, sourceSensor = GlucoseValue.SourceSensor.ZT_PREDICTION, trendArrow = GlucoseValue.TrendArrow.NONE diff --git a/core/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/AutosensDataStore.kt b/core/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/AutosensDataStore.kt index c62250c04a..ba1f172079 100644 --- a/core/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/AutosensDataStore.kt +++ b/core/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/AutosensDataStore.kt @@ -3,9 +3,11 @@ package info.nightscout.androidaps.plugins.iob.iobCobCalculator import androidx.collection.LongSparseArray import androidx.collection.size import info.nightscout.androidaps.annotations.OpenForTesting +import info.nightscout.androidaps.core.R import info.nightscout.androidaps.data.InMemoryGlucoseValue import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.GlucoseValue +import info.nightscout.androidaps.extensions.rawOrSmoothed import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBus @@ -13,11 +15,12 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventBucketedDataCreated import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.T +import info.nightscout.shared.sharedPreferences.SP import kotlin.math.abs import kotlin.math.roundToLong @OpenForTesting -class AutosensDataStore { +class AutosensDataStore(val sp: SP) { private val dataLock = Any() var lastUsed5minCalculation: Boolean? = null // true if used 5min bucketed data @@ -39,7 +42,7 @@ class AutosensDataStore { @Synchronized get fun clone(): AutosensDataStore = - AutosensDataStore().also { + AutosensDataStore(sp).also { synchronized(dataLock) { it.bgReadings = this.bgReadings.toMutableList() it.autosensDataTable = LongSparseArray(this.autosensDataTable.size).apply { putAll(this@AutosensDataStore.autosensDataTable) } @@ -236,6 +239,7 @@ class AutosensDataStore { bucketedData = null return } + val useBgSmoothing = sp.getBoolean(R.string.key_use_data_smoothing, false) val newBucketedData = ArrayList() var currentTime = bgReadings[0].timestamp - bgReadings[0].timestamp % T.mins(5).msecs() val adjustedTime = adjustToReferenceTime(currentTime) @@ -252,9 +256,12 @@ class AutosensDataStore { newBucketedData.add(InMemoryGlucoseValue(newer)) } else { val bgDelta = newer.value - older.value + val bgDeltaRaw = newer.value - older.value + val bgDeltaSmoothed = newer.rawOrSmoothed(useBgSmoothing) - older.rawOrSmoothed(useBgSmoothing) val timeDiffToNew = newer.timestamp - currentTime - val currentBg = newer.value - timeDiffToNew.toDouble() / (newer.timestamp - older.timestamp) * bgDelta - val newBgReading = InMemoryGlucoseValue(currentTime, currentBg.roundToLong().toDouble(), true) + val currentBgRaw = newer.value - timeDiffToNew.toDouble() / (newer.timestamp - older.timestamp) * bgDeltaRaw + val currentBgSmoothed = newer.rawOrSmoothed(useBgSmoothing) - timeDiffToNew.toDouble() / (newer.timestamp - older.timestamp) * bgDeltaSmoothed + val newBgReading = InMemoryGlucoseValue(currentTime, currentBgRaw.roundToLong().toDouble(), currentBgSmoothed.roundToLong().toDouble(),true) newBucketedData.add(newBgReading) //log.debug("BG: " + newBgReading.value + " (" + new Date(newBgReading.date).toLocaleString() + ") Prev: " + older.value + " (" + new Date(older.date).toLocaleString() + ") Newer: " + newer.value + " (" + new Date(newer.date).toLocaleString() + ")"); } @@ -269,6 +276,7 @@ class AutosensDataStore { return } val bData: MutableList = ArrayList() + val useBgSmoothing = sp.getBoolean(R.string.key_use_data_smoothing, false) bData.add(InMemoryGlucoseValue(bgReadings[0])) aapsLogger.debug(LTag.AUTOSENS) { "Adding. bgTime: ${dateUtil.toISOString(bgReadings[0].timestamp)} lastBgTime: none-first-value ${bgReadings[0]}" } var j = 0 @@ -280,39 +288,44 @@ class AutosensDataStore { when { abs(elapsedMinutes) > 8 -> { // interpolate missing data points - var lastBg = bgReadings[i - 1].value + var lastBgRaw = bgReadings[i - 1].value + var lastBgSmoothed = bgReadings[i - 1].rawOrSmoothed(useBgSmoothing) elapsedMinutes = abs(elapsedMinutes) //console.error(elapsed_minutes); var nextBgTime: Long while (elapsedMinutes > 5) { nextBgTime = lastBgTime - 5 * 60 * 1000 j++ - val gapDelta = bgReadings[i].value - lastBg + val gapDeltaRaw = bgReadings[i].value - lastBgRaw + val gapDeltaSmoothed = bgReadings[i].value - lastBgSmoothed //console.error(gapDelta, lastBg, elapsed_minutes); - val nextBg = lastBg + 5.0 / elapsedMinutes * gapDelta - val newBgReading = InMemoryGlucoseValue(nextBgTime, nextBg.roundToLong().toDouble(), true) + val nextBgRaw = lastBgRaw + 5.0 / elapsedMinutes * gapDeltaRaw + val nextBgSmoothed = lastBgSmoothed + 5.0 / elapsedMinutes * gapDeltaSmoothed + val newBgReading = InMemoryGlucoseValue(nextBgTime, nextBgRaw.roundToLong().toDouble(), nextBgSmoothed.roundToLong().toDouble(), true) //console.error("Interpolated", bData[j]); bData.add(newBgReading) aapsLogger.debug(LTag.AUTOSENS) { "Adding. bgTime: ${dateUtil.toISOString(bgTime)} lastBgTime: ${dateUtil.toISOString(lastBgTime)} $newBgReading" } elapsedMinutes -= 5 - lastBg = nextBg + lastBgRaw = nextBgRaw + lastBgSmoothed = nextBgSmoothed lastBgTime = nextBgTime } j++ - val newBgReading = InMemoryGlucoseValue(bgTime, bgReadings[i].value) + val newBgReading = InMemoryGlucoseValue(bgTime, bgReadings[i].value, bgReadings[i].smoothed) bData.add(newBgReading) aapsLogger.debug(LTag.AUTOSENS) { "Adding. bgTime: ${dateUtil.toISOString(bgTime)} lastBgTime: ${dateUtil.toISOString(lastBgTime)} $newBgReading" } } abs(elapsedMinutes) > 2 -> { j++ - val newBgReading = InMemoryGlucoseValue(bgTime, bgReadings[i].value) + val newBgReading = InMemoryGlucoseValue(bgTime, bgReadings[i].value, bgReadings[i].smoothed) bData.add(newBgReading) aapsLogger.debug(LTag.AUTOSENS) { "Adding. bgTime: ${dateUtil.toISOString(bgTime)} lastBgTime: ${dateUtil.toISOString(lastBgTime)} $newBgReading" } } else -> { bData[j].value = (bData[j].value + bgReadings[i].value) / 2 + bData[j].smoothed = (bData[j].rawOrSmoothed(sp) + bgReadings[i].rawOrSmoothed(useBgSmoothing)) / 2 //log.error("***** Average"); } } diff --git a/core/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/GlucoseStatusProvider.kt b/core/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/GlucoseStatusProvider.kt index f722383f5f..b94f3be029 100644 --- a/core/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/GlucoseStatusProvider.kt +++ b/core/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/GlucoseStatusProvider.kt @@ -1,10 +1,13 @@ package info.nightscout.androidaps.plugins.iob.iobCobCalculator import dagger.Reusable +import info.nightscout.androidaps.extensions.rawOrSmoothed +import info.nightscout.androidaps.extensions.useDataSmoothing import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.shared.sharedPreferences.SP import java.util.* import javax.inject.Inject import kotlin.math.roundToLong @@ -13,7 +16,8 @@ import kotlin.math.roundToLong class GlucoseStatusProvider @Inject constructor( private val aapsLogger: AAPSLogger, private val iobCobCalculator: IobCobCalculator, - private val dateUtil: DateUtil + private val dateUtil: DateUtil, + private val sp: SP ) { val glucoseStatusData: GlucoseStatus? @@ -50,7 +54,7 @@ class GlucoseStatusProvider @Inject constructor( val longDeltas = ArrayList() // Use the latest sgv value in the now calculations - nowValueList.add(now.value) + nowValueList.add(now.rawOrSmoothed(sp)) for (i in 1 until sizeRecords) { if (data[i].value > 38) { val then = data[i] @@ -58,15 +62,15 @@ class GlucoseStatusProvider @Inject constructor( val minutesAgo = ((nowDate - thenDate) / (1000.0 * 60)).roundToLong() // multiply by 5 to get the same units as delta, i.e. mg/dL/5m - change = now.value - then.value + change = now.rawOrSmoothed(sp) - then.rawOrSmoothed(sp) val avgDel = change / minutesAgo * 5 aapsLogger.debug(LTag.GLUCOSE, "$then minutesAgo=$minutesAgo avgDelta=$avgDel") // use the average of all data points in the last 2.5m for all further "now" calculations if (0 < minutesAgo && minutesAgo < 2.5) { // Keep and average all values within the last 2.5 minutes - nowValueList.add(then.value) - now.value = average(nowValueList) + nowValueList.add(then.rawOrSmoothed(sp)) + if (useDataSmoothing(sp)) now.smoothed = average(nowValueList) else now.value = average(nowValueList) // short_deltas are calculated from everything ~5-15 minutes ago } else if (2.5 < minutesAgo && minutesAgo < 17.5) { //console.error(minutesAgo, avgDelta); @@ -91,7 +95,7 @@ class GlucoseStatusProvider @Inject constructor( average(lastDeltas) } return GlucoseStatus( - glucose = now.value, + glucose = now.rawOrSmoothed(sp), date = nowDate, noise = 0.0, //for now set to nothing as not all CGMs report noise shortAvgDelta = shortAverageDelta, diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index d4b7d3cf70..ba31afa888 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -602,6 +602,7 @@ Error during last Autotune run Another run of Autotune is detected, run cancelled Application needs bluetooth permission + use_data_smoothing %1$d day diff --git a/database/schemas/info.nightscout.androidaps.database.AppDatabase/22.json b/database/schemas/info.nightscout.androidaps.database.AppDatabase/22.json new file mode 100644 index 0000000000..24835b62a2 --- /dev/null +++ b/database/schemas/info.nightscout.androidaps.database.AppDatabase/22.json @@ -0,0 +1,3599 @@ +{ + "formatVersion": 1, + "database": { + "version": 22, + "identityHash": "e3558dc3bb3136c37dba4f90c97e8bdf", + "entities": [ + { + "tableName": "apsResults", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `algorithm` TEXT NOT NULL, `glucoseStatusJson` TEXT NOT NULL, `currentTempJson` TEXT NOT NULL, `iobDataJson` TEXT NOT NULL, `profileJson` TEXT NOT NULL, `autosensDataJson` TEXT, `mealDataJson` TEXT NOT NULL, `isMicroBolusAllowed` INTEGER, `resultJson` TEXT NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `apsResults`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "algorithm", + "columnName": "algorithm", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "glucoseStatusJson", + "columnName": "glucoseStatusJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currentTempJson", + "columnName": "currentTempJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "iobDataJson", + "columnName": "iobDataJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "profileJson", + "columnName": "profileJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "autosensDataJson", + "columnName": "autosensDataJson", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mealDataJson", + "columnName": "mealDataJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isMicroBolusAllowed", + "columnName": "isMicroBolusAllowed", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "resultJson", + "columnName": "resultJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_apsResults_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_apsResults_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_apsResults_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_apsResults_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "apsResults", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "boluses", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `amount` REAL NOT NULL, `type` TEXT NOT NULL, `isBasalInsulin` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, `insulinLabel` TEXT, `insulinEndTime` INTEGER, `peak` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `boluses`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isBasalInsulin", + "columnName": "isBasalInsulin", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "insulinConfiguration.insulinLabel", + "columnName": "insulinLabel", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "insulinConfiguration.insulinEndTime", + "columnName": "insulinEndTime", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "insulinConfiguration.peak", + "columnName": "peak", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_boluses_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_boluses_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_boluses_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_boluses_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_boluses_temporaryId", + "unique": false, + "columnNames": [ + "temporaryId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_boluses_temporaryId` ON `${TABLE_NAME}` (`temporaryId`)" + }, + { + "name": "index_boluses_pumpId", + "unique": false, + "columnNames": [ + "pumpId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_boluses_pumpId` ON `${TABLE_NAME}` (`pumpId`)" + }, + { + "name": "index_boluses_pumpSerial", + "unique": false, + "columnNames": [ + "pumpSerial" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_boluses_pumpSerial` ON `${TABLE_NAME}` (`pumpSerial`)" + }, + { + "name": "index_boluses_pumpType", + "unique": false, + "columnNames": [ + "pumpType" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_boluses_pumpType` ON `${TABLE_NAME}` (`pumpType`)" + }, + { + "name": "index_boluses_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_boluses_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_boluses_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_boluses_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "boluses", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "bolusCalculatorResults", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `targetBGLow` REAL NOT NULL, `targetBGHigh` REAL NOT NULL, `isf` REAL NOT NULL, `ic` REAL NOT NULL, `bolusIOB` REAL NOT NULL, `wasBolusIOBUsed` INTEGER NOT NULL, `basalIOB` REAL NOT NULL, `wasBasalIOBUsed` INTEGER NOT NULL, `glucoseValue` REAL NOT NULL, `wasGlucoseUsed` INTEGER NOT NULL, `glucoseDifference` REAL NOT NULL, `glucoseInsulin` REAL NOT NULL, `glucoseTrend` REAL NOT NULL, `wasTrendUsed` INTEGER NOT NULL, `trendInsulin` REAL NOT NULL, `cob` REAL NOT NULL, `wasCOBUsed` INTEGER NOT NULL, `cobInsulin` REAL NOT NULL, `carbs` REAL NOT NULL, `wereCarbsUsed` INTEGER NOT NULL, `carbsInsulin` REAL NOT NULL, `otherCorrection` REAL NOT NULL, `wasSuperbolusUsed` INTEGER NOT NULL, `superbolusInsulin` REAL NOT NULL, `wasTempTargetUsed` INTEGER NOT NULL, `totalInsulin` REAL NOT NULL, `percentageCorrection` INTEGER NOT NULL, `profileName` TEXT NOT NULL, `note` TEXT NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `bolusCalculatorResults`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "targetBGLow", + "columnName": "targetBGLow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "targetBGHigh", + "columnName": "targetBGHigh", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "isf", + "columnName": "isf", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "ic", + "columnName": "ic", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "bolusIOB", + "columnName": "bolusIOB", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "wasBolusIOBUsed", + "columnName": "wasBolusIOBUsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "basalIOB", + "columnName": "basalIOB", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "wasBasalIOBUsed", + "columnName": "wasBasalIOBUsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "glucoseValue", + "columnName": "glucoseValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "wasGlucoseUsed", + "columnName": "wasGlucoseUsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "glucoseDifference", + "columnName": "glucoseDifference", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "glucoseInsulin", + "columnName": "glucoseInsulin", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "glucoseTrend", + "columnName": "glucoseTrend", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "wasTrendUsed", + "columnName": "wasTrendUsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "trendInsulin", + "columnName": "trendInsulin", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "cob", + "columnName": "cob", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "wasCOBUsed", + "columnName": "wasCOBUsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "cobInsulin", + "columnName": "cobInsulin", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "carbs", + "columnName": "carbs", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "wereCarbsUsed", + "columnName": "wereCarbsUsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "carbsInsulin", + "columnName": "carbsInsulin", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "otherCorrection", + "columnName": "otherCorrection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "wasSuperbolusUsed", + "columnName": "wasSuperbolusUsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "superbolusInsulin", + "columnName": "superbolusInsulin", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "wasTempTargetUsed", + "columnName": "wasTempTargetUsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "totalInsulin", + "columnName": "totalInsulin", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "percentageCorrection", + "columnName": "percentageCorrection", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "profileName", + "columnName": "profileName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_bolusCalculatorResults_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_bolusCalculatorResults_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_bolusCalculatorResults_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_bolusCalculatorResults_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + }, + { + "name": "index_bolusCalculatorResults_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_bolusCalculatorResults_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_bolusCalculatorResults_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_bolusCalculatorResults_isValid` ON `${TABLE_NAME}` (`isValid`)" + } + ], + "foreignKeys": [ + { + "table": "bolusCalculatorResults", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "carbs", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `duration` INTEGER NOT NULL, `amount` REAL NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `carbs`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_carbs_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_carbs_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_carbs_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_carbs_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_carbs_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_carbs_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + }, + { + "name": "index_carbs_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_carbs_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_carbs_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_carbs_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "carbs", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "effectiveProfileSwitches", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `basalBlocks` TEXT NOT NULL, `isfBlocks` TEXT NOT NULL, `icBlocks` TEXT NOT NULL, `targetBlocks` TEXT NOT NULL, `glucoseUnit` TEXT NOT NULL, `originalProfileName` TEXT NOT NULL, `originalCustomizedName` TEXT NOT NULL, `originalTimeshift` INTEGER NOT NULL, `originalPercentage` INTEGER NOT NULL, `originalDuration` INTEGER NOT NULL, `originalEnd` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, `insulinLabel` TEXT NOT NULL, `insulinEndTime` INTEGER NOT NULL, `peak` INTEGER NOT NULL, FOREIGN KEY(`referenceId`) REFERENCES `effectiveProfileSwitches`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "basalBlocks", + "columnName": "basalBlocks", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isfBlocks", + "columnName": "isfBlocks", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "icBlocks", + "columnName": "icBlocks", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "targetBlocks", + "columnName": "targetBlocks", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "glucoseUnit", + "columnName": "glucoseUnit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "originalProfileName", + "columnName": "originalProfileName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "originalCustomizedName", + "columnName": "originalCustomizedName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "originalTimeshift", + "columnName": "originalTimeshift", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "originalPercentage", + "columnName": "originalPercentage", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "originalDuration", + "columnName": "originalDuration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "originalEnd", + "columnName": "originalEnd", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "insulinConfiguration.insulinLabel", + "columnName": "insulinLabel", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "insulinConfiguration.insulinEndTime", + "columnName": "insulinEndTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "insulinConfiguration.peak", + "columnName": "peak", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_effectiveProfileSwitches_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_effectiveProfileSwitches_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_effectiveProfileSwitches_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_effectiveProfileSwitches_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_effectiveProfileSwitches_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_effectiveProfileSwitches_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + }, + { + "name": "index_effectiveProfileSwitches_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_effectiveProfileSwitches_isValid` ON `${TABLE_NAME}` (`isValid`)" + } + ], + "foreignKeys": [ + { + "table": "effectiveProfileSwitches", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "extendedBoluses", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `duration` INTEGER NOT NULL, `amount` REAL NOT NULL, `isEmulatingTempBasal` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `extendedBoluses`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "isEmulatingTempBasal", + "columnName": "isEmulatingTempBasal", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_extendedBoluses_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_extendedBoluses_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_extendedBoluses_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_extendedBoluses_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_extendedBoluses_endId", + "unique": false, + "columnNames": [ + "endId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_extendedBoluses_endId` ON `${TABLE_NAME}` (`endId`)" + }, + { + "name": "index_extendedBoluses_pumpSerial", + "unique": false, + "columnNames": [ + "pumpSerial" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_extendedBoluses_pumpSerial` ON `${TABLE_NAME}` (`pumpSerial`)" + }, + { + "name": "index_extendedBoluses_pumpId", + "unique": false, + "columnNames": [ + "pumpId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_extendedBoluses_pumpId` ON `${TABLE_NAME}` (`pumpId`)" + }, + { + "name": "index_extendedBoluses_pumpType", + "unique": false, + "columnNames": [ + "pumpType" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_extendedBoluses_pumpType` ON `${TABLE_NAME}` (`pumpType`)" + }, + { + "name": "index_extendedBoluses_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_extendedBoluses_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_extendedBoluses_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_extendedBoluses_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "extendedBoluses", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "glucoseValues", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `raw` REAL, `value` REAL NOT NULL, `smoothed` REAL, `trendArrow` TEXT NOT NULL, `noise` REAL, `sourceSensor` TEXT NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `glucoseValues`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "raw", + "columnName": "raw", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "smoothed", + "columnName": "smoothed", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "trendArrow", + "columnName": "trendArrow", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "noise", + "columnName": "noise", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "sourceSensor", + "columnName": "sourceSensor", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_glucoseValues_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_glucoseValues_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_glucoseValues_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_glucoseValues_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + }, + { + "name": "index_glucoseValues_sourceSensor", + "unique": false, + "columnNames": [ + "sourceSensor" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_glucoseValues_sourceSensor` ON `${TABLE_NAME}` (`sourceSensor`)" + }, + { + "name": "index_glucoseValues_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_glucoseValues_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_glucoseValues_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_glucoseValues_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "glucoseValues", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "profileSwitches", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `basalBlocks` TEXT NOT NULL, `isfBlocks` TEXT NOT NULL, `icBlocks` TEXT NOT NULL, `targetBlocks` TEXT NOT NULL, `glucoseUnit` TEXT NOT NULL, `profileName` TEXT NOT NULL, `timeshift` INTEGER NOT NULL, `percentage` INTEGER NOT NULL, `duration` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, `insulinLabel` TEXT NOT NULL, `insulinEndTime` INTEGER NOT NULL, `peak` INTEGER NOT NULL, FOREIGN KEY(`referenceId`) REFERENCES `profileSwitches`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "basalBlocks", + "columnName": "basalBlocks", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isfBlocks", + "columnName": "isfBlocks", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "icBlocks", + "columnName": "icBlocks", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "targetBlocks", + "columnName": "targetBlocks", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "glucoseUnit", + "columnName": "glucoseUnit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "profileName", + "columnName": "profileName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timeshift", + "columnName": "timeshift", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "percentage", + "columnName": "percentage", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "insulinConfiguration.insulinLabel", + "columnName": "insulinLabel", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "insulinConfiguration.insulinEndTime", + "columnName": "insulinEndTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "insulinConfiguration.peak", + "columnName": "peak", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_profileSwitches_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_profileSwitches_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_profileSwitches_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_profileSwitches_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + }, + { + "name": "index_profileSwitches_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_profileSwitches_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_profileSwitches_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_profileSwitches_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_profileSwitches_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_profileSwitches_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + } + ], + "foreignKeys": [ + { + "table": "profileSwitches", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "temporaryBasals", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `type` TEXT NOT NULL, `isAbsolute` INTEGER NOT NULL, `rate` REAL NOT NULL, `duration` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `temporaryBasals`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isAbsolute", + "columnName": "isAbsolute", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "rate", + "columnName": "rate", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_temporaryBasals_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_temporaryBasals_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_temporaryBasals_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + }, + { + "name": "index_temporaryBasals_pumpType", + "unique": false, + "columnNames": [ + "pumpType" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_pumpType` ON `${TABLE_NAME}` (`pumpType`)" + }, + { + "name": "index_temporaryBasals_endId", + "unique": false, + "columnNames": [ + "endId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_endId` ON `${TABLE_NAME}` (`endId`)" + }, + { + "name": "index_temporaryBasals_pumpSerial", + "unique": false, + "columnNames": [ + "pumpSerial" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_pumpSerial` ON `${TABLE_NAME}` (`pumpSerial`)" + }, + { + "name": "index_temporaryBasals_temporaryId", + "unique": false, + "columnNames": [ + "temporaryId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_temporaryId` ON `${TABLE_NAME}` (`temporaryId`)" + }, + { + "name": "index_temporaryBasals_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_temporaryBasals_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "temporaryBasals", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "temporaryTargets", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `reason` TEXT NOT NULL, `highTarget` REAL NOT NULL, `lowTarget` REAL NOT NULL, `duration` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `temporaryTargets`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "reason", + "columnName": "reason", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "highTarget", + "columnName": "highTarget", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "lowTarget", + "columnName": "lowTarget", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_temporaryTargets_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryTargets_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_temporaryTargets_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryTargets_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_temporaryTargets_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryTargets_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + }, + { + "name": "index_temporaryTargets_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryTargets_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_temporaryTargets_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryTargets_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "temporaryTargets", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "therapyEvents", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `duration` INTEGER NOT NULL, `type` TEXT NOT NULL, `note` TEXT, `enteredBy` TEXT, `glucose` REAL, `glucoseType` TEXT, `glucoseUnit` TEXT NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `therapyEvents`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "enteredBy", + "columnName": "enteredBy", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "glucose", + "columnName": "glucose", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "glucoseType", + "columnName": "glucoseType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "glucoseUnit", + "columnName": "glucoseUnit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_therapyEvents_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_therapyEvents_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_therapyEvents_type", + "unique": false, + "columnNames": [ + "type" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_therapyEvents_type` ON `${TABLE_NAME}` (`type`)" + }, + { + "name": "index_therapyEvents_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_therapyEvents_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + }, + { + "name": "index_therapyEvents_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_therapyEvents_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_therapyEvents_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_therapyEvents_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_therapyEvents_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_therapyEvents_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "therapyEvents", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "totalDailyDoses", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `basalAmount` REAL NOT NULL, `bolusAmount` REAL NOT NULL, `totalAmount` REAL NOT NULL, `carbs` REAL NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `totalDailyDoses`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "basalAmount", + "columnName": "basalAmount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "bolusAmount", + "columnName": "bolusAmount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "totalAmount", + "columnName": "totalAmount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "carbs", + "columnName": "carbs", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_totalDailyDoses_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_totalDailyDoses_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_totalDailyDoses_pumpId", + "unique": false, + "columnNames": [ + "pumpId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_totalDailyDoses_pumpId` ON `${TABLE_NAME}` (`pumpId`)" + }, + { + "name": "index_totalDailyDoses_pumpType", + "unique": false, + "columnNames": [ + "pumpType" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_totalDailyDoses_pumpType` ON `${TABLE_NAME}` (`pumpType`)" + }, + { + "name": "index_totalDailyDoses_pumpSerial", + "unique": false, + "columnNames": [ + "pumpSerial" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_totalDailyDoses_pumpSerial` ON `${TABLE_NAME}` (`pumpSerial`)" + }, + { + "name": "index_totalDailyDoses_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_totalDailyDoses_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_totalDailyDoses_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_totalDailyDoses_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_totalDailyDoses_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_totalDailyDoses_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "totalDailyDoses", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "apsResultLinks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `apsResultId` INTEGER NOT NULL, `smbId` INTEGER, `tbrId` INTEGER, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`apsResultId`) REFERENCES `apsResults`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`smbId`) REFERENCES `boluses`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`tbrId`) REFERENCES `temporaryBasals`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`referenceId`) REFERENCES `apsResultLinks`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "apsResultId", + "columnName": "apsResultId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "smbId", + "columnName": "smbId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "tbrId", + "columnName": "tbrId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_apsResultLinks_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_apsResultLinks_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_apsResultLinks_apsResultId", + "unique": false, + "columnNames": [ + "apsResultId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_apsResultLinks_apsResultId` ON `${TABLE_NAME}` (`apsResultId`)" + }, + { + "name": "index_apsResultLinks_smbId", + "unique": false, + "columnNames": [ + "smbId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_apsResultLinks_smbId` ON `${TABLE_NAME}` (`smbId`)" + }, + { + "name": "index_apsResultLinks_tbrId", + "unique": false, + "columnNames": [ + "tbrId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_apsResultLinks_tbrId` ON `${TABLE_NAME}` (`tbrId`)" + } + ], + "foreignKeys": [ + { + "table": "apsResults", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "apsResultId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "boluses", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "smbId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "temporaryBasals", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "tbrId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "apsResultLinks", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "multiwaveBolusLinks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `bolusId` INTEGER NOT NULL, `extendedBolusId` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`bolusId`) REFERENCES `boluses`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`extendedBolusId`) REFERENCES `extendedBoluses`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`referenceId`) REFERENCES `multiwaveBolusLinks`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "bolusId", + "columnName": "bolusId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "extendedBolusId", + "columnName": "extendedBolusId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_multiwaveBolusLinks_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_multiwaveBolusLinks_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_multiwaveBolusLinks_bolusId", + "unique": false, + "columnNames": [ + "bolusId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_multiwaveBolusLinks_bolusId` ON `${TABLE_NAME}` (`bolusId`)" + }, + { + "name": "index_multiwaveBolusLinks_extendedBolusId", + "unique": false, + "columnNames": [ + "extendedBolusId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_multiwaveBolusLinks_extendedBolusId` ON `${TABLE_NAME}` (`extendedBolusId`)" + } + ], + "foreignKeys": [ + { + "table": "boluses", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "bolusId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "extendedBoluses", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "extendedBolusId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "multiwaveBolusLinks", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "preferenceChanges", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `key` TEXT NOT NULL, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "versionChanges", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `versionCode` INTEGER NOT NULL, `versionName` TEXT NOT NULL, `gitRemote` TEXT, `commitHash` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "versionCode", + "columnName": "versionCode", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "versionName", + "columnName": "versionName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gitRemote", + "columnName": "gitRemote", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "commitHash", + "columnName": "commitHash", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "userEntry", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `action` TEXT NOT NULL, `source` TEXT NOT NULL, `note` TEXT NOT NULL, `values` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "action", + "columnName": "action", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "source", + "columnName": "source", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_userEntry_source", + "unique": false, + "columnNames": [ + "source" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_userEntry_source` ON `${TABLE_NAME}` (`source`)" + }, + { + "name": "index_userEntry_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_userEntry_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "foods", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `name` TEXT NOT NULL, `category` TEXT, `subCategory` TEXT, `portion` REAL NOT NULL, `carbs` INTEGER NOT NULL, `fat` INTEGER, `protein` INTEGER, `energy` INTEGER, `unit` TEXT NOT NULL, `gi` INTEGER, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `foods`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "subCategory", + "columnName": "subCategory", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "portion", + "columnName": "portion", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "carbs", + "columnName": "carbs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fat", + "columnName": "fat", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "protein", + "columnName": "protein", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "energy", + "columnName": "energy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unit", + "columnName": "unit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gi", + "columnName": "gi", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_foods_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_foods_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_foods_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_foods_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + }, + { + "name": "index_foods_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_foods_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_foods_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_foods_isValid` ON `${TABLE_NAME}` (`isValid`)" + } + ], + "foreignKeys": [ + { + "table": "foods", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "deviceStatus", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `device` TEXT, `pump` TEXT, `enacted` TEXT, `suggested` TEXT, `iob` TEXT, `uploaderBattery` INTEGER NOT NULL, `configuration` TEXT, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "device", + "columnName": "device", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pump", + "columnName": "pump", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "enacted", + "columnName": "enacted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "suggested", + "columnName": "suggested", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iob", + "columnName": "iob", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploaderBattery", + "columnName": "uploaderBattery", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "configuration", + "columnName": "configuration", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_deviceStatus_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_deviceStatus_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_deviceStatus_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_deviceStatus_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + }, + { + "name": "index_deviceStatus_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_deviceStatus_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "offlineEvents", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `reason` TEXT NOT NULL, `duration` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `offlineEvents`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "reason", + "columnName": "reason", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_offlineEvents_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_offlineEvents_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_offlineEvents_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_offlineEvents_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_offlineEvents_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_offlineEvents_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + }, + { + "name": "index_offlineEvents_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_offlineEvents_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_offlineEvents_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_offlineEvents_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "offlineEvents", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(43, 'e3558dc3bb3136c37dba4f90c97e8bdf')" + ] + } +} \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/AppDatabase.kt b/database/src/main/java/info/nightscout/androidaps/database/AppDatabase.kt index 948aa705d6..58fc350cec 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/AppDatabase.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/AppDatabase.kt @@ -6,7 +6,7 @@ import androidx.room.TypeConverters import info.nightscout.androidaps.database.daos.* import info.nightscout.androidaps.database.entities.* -const val DATABASE_VERSION = 21 +const val DATABASE_VERSION = 22 @Database(version = DATABASE_VERSION, entities = [APSResult::class, Bolus::class, BolusCalculatorResult::class, Carbs::class, diff --git a/database/src/main/java/info/nightscout/androidaps/database/DatabaseModule.kt b/database/src/main/java/info/nightscout/androidaps/database/DatabaseModule.kt index 4f2b1be88d..cd1f92365d 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/DatabaseModule.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/DatabaseModule.kt @@ -1,6 +1,8 @@ package info.nightscout.androidaps.database import android.content.Context +import android.database.Cursor +import android.database.sqlite.SQLiteException import androidx.room.Room import androidx.room.RoomDatabase.Callback import androidx.room.migration.Migration @@ -27,6 +29,7 @@ open class DatabaseModule { // .addMigrations(migration7to8) // .addMigrations(migration11to12) .addMigrations(migration20to21) + .addMigrations(migration21to22) .addCallback(object : Callback() { override fun onOpen(db: SupportSQLiteDatabase) { super.onOpen(db) @@ -69,4 +72,29 @@ open class DatabaseModule { } } + private val migration21to22 = object : Migration(21,22) { + override fun migrate(database: SupportSQLiteDatabase) { + addColumnIfNotExists(database,"glucoseValues", "smoothed", "REAL") + dropCustomIndexes(database) + } + } + + private fun addColumnIfNotExists(db: SupportSQLiteDatabase, table: String, columnToCheck: String, columnTypeDefinition: String) { + if(!columnExistsInTable(db, table, columnToCheck)) { + db.execSQL("ALTER TABLE `$table` ADD COLUMN `$columnToCheck` $columnTypeDefinition") + } + } + + private fun columnExistsInTable(db: SupportSQLiteDatabase?, table: String, columnToCheck: String?): Boolean { + var cursor: Cursor? = null + return try { + cursor = db?.query("SELECT * FROM $table LIMIT 0", null) + cursor?.getColumnIndex(columnToCheck) !== -1 + } catch (Exp: SQLiteException) { + false + } finally { + cursor?.close() + } + } + } \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/entities/GlucoseValue.kt b/database/src/main/java/info/nightscout/androidaps/database/entities/GlucoseValue.kt index 00979a554c..e70e477257 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/entities/GlucoseValue.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/entities/GlucoseValue.kt @@ -39,6 +39,7 @@ data class GlucoseValue( override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(), var raw: Double?, var value: Double, + var smoothed: Double?, var trendArrow: TrendArrow, var noise: Double?, var sourceSensor: SourceSensor diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/CgmSourceTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/CgmSourceTransaction.kt index 382aed5cde..9f5d510f8f 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/transactions/CgmSourceTransaction.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/CgmSourceTransaction.kt @@ -3,6 +3,9 @@ package info.nightscout.androidaps.database.transactions import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.entities.ProfileSwitch import info.nightscout.androidaps.database.entities.TherapyEvent +import java.util.ArrayList +import kotlin.math.max +import kotlin.math.round /** * Inserts data from a CGM source into the database @@ -11,7 +14,9 @@ class CgmSourceTransaction( private val glucoseValues: List, private val calibrations: List, private val sensorInsertionTime: Long?, - private val syncer: Boolean = false // caller is not native source ie. NS + private val syncer: Boolean = false, // caller is not native source ie. NS + private var bgReadings: List = listOf(), + private val updateWindow: Int = 10 // max number of smoothed glucose values to be updated per cycle to avoid updating the database with all entries stored in bgReadings // syncer is allowed create records // update synchronization ID ) : Transaction() { @@ -24,6 +29,7 @@ class CgmSourceTransaction( timestamp = it.timestamp, raw = it.raw, value = it.value, + smoothed = it.smoothed, noise = it.noise, trendArrow = it.trendArrow, sourceSensor = it.sourceSensor @@ -35,6 +41,13 @@ class CgmSourceTransaction( current?.let { existing -> glucoseValue.interfaceIDs.nightscoutId = existing.interfaceIDs.nightscoutId } // preserve invalidated status (user may delete record in UI) current?.let { existing -> glucoseValue.isValid = existing.isValid } + // calculate smoothed values and update DB entries + bgReadings += database.glucoseValueDao.compatGetBgReadingsDataFromTime(glucoseValue.timestamp - 125 * 60 * 1000, glucoseValue.timestamp) //MP Get all readings from up to 125 mins ago + .blockingGet() + bgReadings += glucoseValue + bgReadings = smooth(bgReadings.reversed(), updateWindow) //reverse the list as the smoothing function expects the 0th entry to be the most recent one + //bgReadings += database.glucoseValueDao.findByTimestampAndSensor(it.timestamp, it.sourceSensor) + glucoseValue.smoothed = bgReadings[0].smoothed when { // new record, create new current == null -> { @@ -55,6 +68,12 @@ class CgmSourceTransaction( } } } + // Update the smoothed glucose values of the entries included in bgReadings + bgReadings = bgReadings.reversed().takeLast(updateWindow) // reverse again so that in the DB, the newest values have the largest ID (for cosmetic and logical reasons :P). Also, keep only the last 10 entries to avoid unnecessarily updating older values that + for (i in bgReadings) { + database.glucoseValueDao.updateExistingEntry(i) + result.updated.add(i) + } calibrations.forEach { if (database.therapyEventDao.findByTimestamp(TherapyEvent.Type.FINGER_STICK_BG_VALUE, it.timestamp) == null) { val therapyEvent = TherapyEvent( @@ -81,10 +100,119 @@ class CgmSourceTransaction( return result } + private fun smooth(Data: List, updateWindow: Int): List { + /** + * TSUNAMI DATA SMOOTHING CORE + * + * Calculated a weighted average of 1st and 2nd order exponential smoothing functions + * to reduce the effect of sensor noise on APS performance. The weighted average + * is a compromise between the fast response to changing BGs at the cost of smoothness + * as offered by 1st order exponential smoothing, and the predictive, trend-sensitive but + * slower-to-respond smoothing as offered by 2nd order functions. + * + */ + val sizeRecords = Data.size + val o1_sBG: ArrayList = ArrayList() //MP array for 1st order Smoothed Blood Glucose + val o2_sBG: ArrayList = ArrayList() //MP array for 2nd order Smoothed Blood Glucose + val o2_sD: ArrayList = ArrayList() //MP array for 2nd order Smoothed delta + val ssBG: ArrayList = ArrayList() //MP array for weighted averaged, doubly smoothed Blood Glucose + //val ssD: ArrayList = ArrayList() //MP array for deltas of doubly smoothed Blood Glucose + var windowSize = 25 //MP number of bg readings to include in smoothing window + val o1_weight = 0.4 + val o1_a = 0.5 + val o2_a = 0.4 + val o2_b = 1.0 + var insufficientSmoothingData = false + + // ADJUST SMOOTHING WINDOW TO ONLY INCLUDE VALID READINGS + // Valid readings include: + // - Values that actually exist (windowSize may not be larger than sizeRecords) + // - Values that come in approx. every 5 min. If the time gap between two readings is larger, this is likely due to a sensor error or warmup of a new sensor.d + // - Values that are not 38 mg/dl; 38 mg/dl reflects an xDrip error state (according to a comment in determine-basal.js) + + //MP: Adjust smoothing window if database size is smaller than the default value + 1 (+1 because the reading before the oldest reading to be smoothed will be used in the calculations + if (sizeRecords <= windowSize) { //MP standard smoothing window + windowSize = + (sizeRecords - 1).coerceAtLeast(0) //MP Adjust smoothing window to the size of database if it is smaller than the original window size; -1 to always have at least one older value to compare against as a buffer to prevent app crashes + } + + //MP: Adjust smoothing window further if a gap in the BG database is detected, e.g. due to sensor errors of sensor swaps, or if 38 mg/dl are reported (xDrip error state) + for (i in 0 until windowSize) { + if (Math.round((Data[i].timestamp - Data[i + 1].timestamp) / (1000.0 * 60)) >= 12) { //MP: 12 min because a missed reading (i.e. readings coming in after 10 min) can occur for various reasons, like walking away from the phone or reinstalling AAPS + //if (Math.round((data.get(i).date - data.get(i + 1).date) / 60000L) <= 7) { //MP crashes the app, useful for testing + windowSize = + i + 1 //MP: If time difference between two readings exceeds 7 min, adjust windowSize to *include* the more recent reading (i = reading; +1 because windowSize reflects number of valid readings); + break + } else if (Data[i].value == 38.0) { + windowSize = i //MP: 38 mg/dl reflects an xDrip error state; Chain of valid readings ends here, *exclude* this value (windowSize = i; i + 1 would include the current value) + break + } + } + + // CALCULATE SMOOTHING WINDOW - 1st order exponential smoothing + o1_sBG.clear() // MP reset smoothed bg array + + if (windowSize >= 4) { //MP: Require a valid windowSize of at least 4 readings + o1_sBG.add(Data[windowSize - 1].value) //MP: Initialise smoothing with the oldest valid data point + for (i in 0 until windowSize) { //MP calculate smoothed bg window of valid readings + o1_sBG.add( + 0, + o1_sBG[0] + o1_a * (Data[windowSize - 1 - i].value - o1_sBG[0]) + ) //MP build array of 1st order smoothed bgs + } + } else { + insufficientSmoothingData = true + } + + // CALCULATE SMOOTHING WINDOW - 2nd order exponential smoothing + if (windowSize >= 4) { //MP: Require a valid windowSize of at least 4 readings + o2_sBG.add(Data[windowSize - 1].value) //MP Start 2nd order exponential data smoothing with the oldest valid bg + o2_sD.add(Data[windowSize - 2].value - Data[windowSize - 1].value) //MP Start 2nd order exponential data smoothing with the oldest valid delta + for (i in 0 until windowSize - 1) { //MP calculated smoothed bg window of last 1 h + o2_sBG.add( + 0, + o2_a * Data[windowSize - 2 - i].value + (1 - o2_a) * (o2_sBG[0] + o2_sD[0]) + ) //MP build array of 2nd order smoothed bgs; windowSize-1 is the oldest valid bg value, so windowSize-2 is from when on the smoothing begins; + o2_sD.add( + 0, + o2_b * (o2_sBG[0] - o2_sBG[1]) + (1 - o2_b) * o2_sD[0] + ) //MP build array of 1st order smoothed bgs + } + } else { + insufficientSmoothingData = true + } + + // CALCULATE WEIGHTED AVERAGES OF GLUCOSE & DELTAS + //ssBG.clear() // MP reset doubly smoothed bg array + //ssD.clear() // MP reset doubly smoothed delta array + + if (!insufficientSmoothingData) { //MP Build doubly smoothed array only if there is enough valid readings + for (i in o2_sBG.indices) { //MP calculated doubly smoothed bg of all o1/o2 smoothed data available; o2 & o1 smoothbg array sizes are equal in size, so only one is used as a condition here + ssBG.add(o1_weight * o1_sBG[i] + (1 - o1_weight) * o2_sBG[i]) //MP build array of doubly smoothed bgs + } + /* + for (i in 0 until ssBG.size - 1) { + ssD.add(ssBG[i] - ssBG[i + 1]) //MP build array of doubly smoothed bg deltas + } + */ + for (i in 0 until minOf(ssBG.size, updateWindow)) { // noise at the beginning of the smoothing window is the greatest, so only include the 10 most recent values in the output + Data[i].smoothed = max(round(ssBG[i]), 39.0) //Make 39 the smallest value as smaller values trigger errors (xDrip error state = 38) + } + } else { + for (i in 0 until minOf(Data.size, updateWindow)) { // noise at the beginning of the smoothing window is the greatest, so only include the 10 most recent values in the output + Data[i].smoothed = max(Data[i].value, 39.0) // if insufficient smoothing data, copy 'value' into 'smoothed' data column so that it isn't empty; Make 39 the smallest value as smaller + // values trigger errors (xDrip error state = 38) + } + } + + return Data + } + data class TransactionGlucoseValue( val timestamp: Long, val value: Double, val raw: Double?, + val smoothed: Double?, val noise: Double?, val trendArrow: GlucoseValue.TrendArrow, val nightscoutId: String? = null, From 11852b8ff1ffc26554554cf382620080f1f7af19 Mon Sep 17 00:00:00 2001 From: Selcuk Kekec Date: Sat, 20 May 2023 08:52:05 -0600 Subject: [PATCH 5/7] Integration of raw and smoothed data into chart --- .../plugins/general/overview/OverviewData.kt | 1 + .../general/overview/graphData/GraphData.kt | 3 +- .../graphExtensions/GlucoseValueDataPoint.kt | 39 ++++++++++++++++--- .../workflow/PrepareBgDataWorker.kt | 5 ++- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt index 6be9c34aef..3b7d935997 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt @@ -249,6 +249,7 @@ class OverviewData @Inject constructor( var maxBgValue = Double.MIN_VALUE var bucketedGraphSeries: PointsWithLabelGraphSeries = PointsWithLabelGraphSeries() var bgReadingGraphSeries: PointsWithLabelGraphSeries = PointsWithLabelGraphSeries() + var bgReadingRawGraphSeries: PointsWithLabelGraphSeries = PointsWithLabelGraphSeries() var predictionsGraphSeries: PointsWithLabelGraphSeries = PointsWithLabelGraphSeries() val basalScale = Scale() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt index 7b34ba26c3..b10759f9d8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt @@ -53,6 +53,7 @@ class GraphData( if (units == GlucoseUnit.MGDL) 180.0 else 10.0 } else overviewData.maxBgValue minY = 0.0 + if (!overviewData.bgReadingRawGraphSeries.isEmpty) addSeries(overviewData.bgReadingRawGraphSeries) addSeries(overviewData.bgReadingGraphSeries) if (addPredictions) addSeries(overviewData.predictionsGraphSeries) overviewData.bgReadingGraphSeries.setOnDataPointTapListener { _, dataPoint -> @@ -93,7 +94,7 @@ class GraphData( maxY = maxOf(maxY, overviewData.maxTreatmentsValue) addSeries(overviewData.treatmentsSeries) overviewData.treatmentsSeries.setOnDataPointTapListener { _, dataPoint -> - if (dataPoint is BolusDataPoint) ToastUtils.showToastInUiThread(context, dataPoint.label) + if (dataPoint is BolusDataPoint) ToastUtils.graphicalToast(context, dataPoint.label, R.drawable.ic_bolus) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/GlucoseValueDataPoint.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/GlucoseValueDataPoint.kt index d0d4d3862f..2bdd4621b7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/GlucoseValueDataPoint.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/GlucoseValueDataPoint.kt @@ -5,6 +5,7 @@ import info.nightscout.androidaps.Constants import info.nightscout.androidaps.core.R import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.extensions.rawOrSmoothed +import info.nightscout.androidaps.extensions.useDataSmoothing import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction @@ -16,26 +17,54 @@ class GlucoseValueDataPoint( val data: GlucoseValue, private val defaultValueHelper: DefaultValueHelper, private val profileFunction: ProfileFunction, - private val rh: ResourceHelper, - private val sp: SP + private val rh: ResourceHelper ) : DataPointWithLabelInterface { + private var useSmoothed : Boolean = false + private var isAdditional : Boolean = false + + constructor(data: GlucoseValue, + defaultValueHelper: DefaultValueHelper, + profileFunction: ProfileFunction, + rh: ResourceHelper, + useSmoothed: Boolean, + isAdditional: Boolean = false) : this( + data, + defaultValueHelper, + profileFunction, + rh) { + this.useSmoothed = useSmoothed + this.isAdditional = isAdditional + } + + constructor(data: GlucoseValue, + defaultValueHelper: DefaultValueHelper, + profileFunction: ProfileFunction, + rh: ResourceHelper, + sp: SP) : this( + data, + defaultValueHelper, + profileFunction, + rh) { + useSmoothed = useDataSmoothing(sp) + } fun valueToUnits(units: GlucoseUnit): Double = - if (units == GlucoseUnit.MGDL) data.rawOrSmoothed(sp) else data.rawOrSmoothed(sp) * Constants.MGDL_TO_MMOLL + if (units == GlucoseUnit.MGDL) data.rawOrSmoothed(useSmoothed) else data.rawOrSmoothed(useSmoothed) * Constants.MGDL_TO_MMOLL override fun getX(): Double = data.timestamp.toDouble() override fun getY(): Double = valueToUnits(profileFunction.getUnits()) override fun setY(y: Double) {} - override val label: String = Profile.toCurrentUnitsString(profileFunction, data.rawOrSmoothed(sp)) + override val label: String = Profile.toCurrentUnitsString(profileFunction, data.rawOrSmoothed(useSmoothed)) override val duration = 0L override val shape get() = if (isPrediction) PointsWithLabelGraphSeries.Shape.PREDICTION else PointsWithLabelGraphSeries.Shape.BG - override val size = 1f + override val size get() = if (isAdditional) 0.5f else 1f override fun color(context: Context?): Int { val units = profileFunction.getUnits() val lowLine = defaultValueHelper.determineLowLine() val highLine = defaultValueHelper.determineHighLine() return when { + isAdditional -> rh.gac(context, R.attr.defaultTextColor) isPrediction -> predictionColor(context) valueToUnits(units) < lowLine -> rh.gac(context, R.attr.bgLow) valueToUnits(units) > highLine -> rh.gac(context, R.attr.highColor) diff --git a/app/src/main/java/info/nightscout/androidaps/workflow/PrepareBgDataWorker.kt b/app/src/main/java/info/nightscout/androidaps/workflow/PrepareBgDataWorker.kt index d0fa6ec781..f6d107635a 100644 --- a/app/src/main/java/info/nightscout/androidaps/workflow/PrepareBgDataWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/workflow/PrepareBgDataWorker.kt @@ -54,13 +54,16 @@ class PrepareBgDataWorker( data.overviewData.maxBgValue = Double.MIN_VALUE data.overviewData.bgReadingsArray = repository.compatGetBgReadingsDataFromTime(data.overviewData.fromTime, data.overviewData.toTime, false).blockingGet() val bgListArray: MutableList = ArrayList() + val bgListRawArray: MutableList = ArrayList() for (bg in data.overviewData.bgReadingsArray) { if (bg.timestamp < data.overviewData.fromTime || bg.timestamp > data.overviewData.toTime) continue if (bg.rawOrSmoothed(useSmoothed) > data.overviewData.maxBgValue) data.overviewData.maxBgValue = bg.rawOrSmoothed(useSmoothed) - bgListArray.add(GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh, sp)) + bgListArray.add(GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh, useSmoothed)) + if (useSmoothed) bgListRawArray.add(GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh, false, isAdditional = true)) } bgListArray.sortWith { o1: DataPointWithLabelInterface, o2: DataPointWithLabelInterface -> o1.x.compareTo(o2.x) } data.overviewData.bgReadingGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] }) + data.overviewData.bgReadingRawGraphSeries = PointsWithLabelGraphSeries(Array(bgListRawArray.size) { i -> bgListRawArray[i] }) data.overviewData.maxBgValue = Profile.fromMgdlToUnits(data.overviewData.maxBgValue, profileFunction.getUnits()) if (defaultValueHelper.determineHighLine() > data.overviewData.maxBgValue) data.overviewData.maxBgValue = defaultValueHelper.determineHighLine() data.overviewData.maxBgValue = addUpperChartMargin(data.overviewData.maxBgValue) From e8b0670db368b996dd301a0e3affd5fb85e3bfe2 Mon Sep 17 00:00:00 2001 From: Selcuk Kekec Date: Sat, 20 May 2023 08:59:13 -0600 Subject: [PATCH 6/7] Smoothing: RAW BG switch in graph menu integrated --- .../androidaps/activities/HistoryBrowseActivity.kt | 2 +- .../plugins/general/overview/OverviewFragment.kt | 2 +- .../androidaps/plugins/general/overview/OverviewMenus.kt | 2 ++ .../plugins/general/overview/graphData/GraphData.kt | 6 +++--- app/src/main/res/values-de-rDE/strings.xml | 9 +++++++++ app/src/main/res/values/strings.xml | 2 ++ 6 files changed, 18 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.kt index 73547e31c2..a7b0acce2f 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.kt @@ -291,7 +291,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { val graphData = GraphData(injector, binding.bgGraph, historyBrowserData.overviewData) val menuChartSettings = overviewMenus.setting graphData.addInRangeArea(historyBrowserData.overviewData.fromTime, historyBrowserData.overviewData.endTime, defaultValueHelper.determineLowLine(), defaultValueHelper.determineHighLine()) - graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal], context) + graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal], menuChartSettings[0][OverviewMenus.CharType.RAW.ordinal], context) if (buildHelper.isDev()) graphData.addBucketedData() graphData.addTreatments(context) graphData.addEps(context, 0.95) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt index 236b03f49a..979c325eb0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt @@ -1010,7 +1010,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList val menuChartSettings = overviewMenus.setting if (menuChartSettings.isEmpty()) return graphData.addInRangeArea(overviewData.fromTime, overviewData.endTime, defaultValueHelper.determineLowLine(), defaultValueHelper.determineHighLine()) - graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal], context) + graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal], menuChartSettings[0][OverviewMenus.CharType.RAW.ordinal], context) if (buildHelper.isDev()) graphData.addBucketedData() graphData.addTreatments(context) graphData.addEps(context, 0.95) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt index 2431dc5f61..3a96d8725a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt @@ -38,6 +38,7 @@ class OverviewMenus @Inject constructor( ) { enum class CharType(@StringRes val nameId: Int, @AttrRes val attrId: Int, @AttrRes val attrTextId: Int, val primary: Boolean, val secondary: Boolean, @StringRes val shortnameId: Int) { + RAW(R.string.overview_show_raw_bg, R.attr.defaultTextColor, R.attr.menuTextColorInverse, primary = true, secondary = false, shortnameId = R.string.activity_shortname), PRE(R.string.overview_show_predictions, R.attr.predictionColor, R.attr.menuTextColor, primary = true, secondary = false, shortnameId = R.string.prediction_shortname), TREAT(R.string.overview_show_treatments, R.attr.cobColor, R.attr.menuTextColor, primary = true, secondary = false, shortnameId = R.string.treatments_shortname), BAS(R.string.overview_show_basals, R.attr.basal, R.attr.menuTextColor, primary = true, secondary = false, shortnameId = R.string.basal_shortname), @@ -126,6 +127,7 @@ class OverviewMenus @Inject constructor( if (g == 0 && !m.primary) return@forEach if (g > 0 && !m.secondary) return@forEach var insert = true + if (m == CharType.RAW) insert = sp.getBoolean(info.nightscout.androidaps.core.R.string.key_use_data_smoothing, false) if (m == CharType.PRE) insert = predictionsAvailable if (m == CharType.DEVSLOPE) insert = buildHelper.isDev() if (used.contains(m.ordinal)) insert = false diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt index b10759f9d8..26c2e0da37 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt @@ -48,12 +48,12 @@ class GraphData( addSeries(overviewData.bucketedGraphSeries) } - fun addBgReadings(addPredictions: Boolean, context: Context?) { + fun addBgReadings(addPredictions: Boolean, addRawBg: Boolean, context: Context?) { maxY = if (overviewData.bgReadingsArray.isEmpty()) { if (units == GlucoseUnit.MGDL) 180.0 else 10.0 } else overviewData.maxBgValue minY = 0.0 - if (!overviewData.bgReadingRawGraphSeries.isEmpty) addSeries(overviewData.bgReadingRawGraphSeries) + if (addRawBg && !overviewData.bgReadingRawGraphSeries.isEmpty) addSeries(overviewData.bgReadingRawGraphSeries) addSeries(overviewData.bgReadingGraphSeries) if (addPredictions) addSeries(overviewData.predictionsGraphSeries) overviewData.bgReadingGraphSeries.setOnDataPointTapListener { _, dataPoint -> @@ -94,7 +94,7 @@ class GraphData( maxY = maxOf(maxY, overviewData.maxTreatmentsValue) addSeries(overviewData.treatmentsSeries) overviewData.treatmentsSeries.setOnDataPointTapListener { _, dataPoint -> - if (dataPoint is BolusDataPoint) ToastUtils.graphicalToast(context, dataPoint.label, R.drawable.ic_bolus) + if (dataPoint is BolusDataPoint) ToastUtils.infoToast(context, dataPoint.label) } } diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index 19396b422b..65c5bfebc5 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -385,6 +385,7 @@ BAS DEV ACT + RAW -BGI ABS DEVSLOPE @@ -555,6 +556,7 @@ Unterkategorie Bolus wird nur aufgezeichnet (Pumpe gibt kein Insulin ab!) SMB von der Pumpe abgegeben + Raw BZ Aktivität Blutzuckerwirkung Sensitivität @@ -1046,4 +1048,11 @@ Unerwartetes Verhalten. Blockiert durch Verbindungsoptionen (keine Uhr verbunden) Fehler beim Anfordern der Erlaubnis + + + use_data_smoothing + Glättet eingehende Sensordaten im Hauptdiagramm. Empfohlen, wenn der Sensor die Sensormesswerte nicht selbst glättet und SMB immer aktiviert werden sollen. + Glättung eingehender Sensordaten + Sensordatenglättung ist aktiviert. + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 999c84be37..984eded728 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -464,6 +464,7 @@ BAS DEV ACT + RAW -BGI ABS DEVSLOPE @@ -675,6 +676,7 @@ Subcategory Bolus will be recorded only (not delivered by pump) SMB set by pump + Raw BG Activity Blood Glucose Impact Sensitivity From 313f0d603e3a3174bad2befe301201aacc503cf6 Mon Sep 17 00:00:00 2001 From: jgslade Date: Wed, 28 Jun 2023 06:59:56 -0600 Subject: [PATCH 7/7] Update Connection.kt --- .../dash/driver/comm/session/Connection.kt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Connection.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Connection.kt index 7200aba9fc..c56c69b237 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Connection.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Connection.kt @@ -96,7 +96,6 @@ class Connection( connectionWaitCond.timeoutMs = newTimeout } podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTED - _connectionWaitCond = null val discoverer = ServiceDiscoverer(aapsLogger, gatt, bleCommCallbacks, this) val discovered = discoverer.discoverServices(connectionWaitCond) @@ -122,21 +121,24 @@ class Connection( cmdBleIO.hello() cmdBleIO.readyToRead() dataBleIO.readyToRead() + _connectionWaitCond = null } @Synchronized fun disconnect(closeGatt: Boolean) { aapsLogger.debug(LTag.PUMPBTCOMM, "Disconnecting closeGatt=$closeGatt") - podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.DISCONNECTED - if (closeGatt) { + if (closeGatt == false && gattConnection != null) { + // Disconnect first, then close gatt + gattConnection?.disconnect() + } else { + // Call with closeGatt=true only when ble is already disconnected or there is no connection gattConnection?.close() + bleCommCallbacks.resetConnection() gattConnection = null - } else { - gattConnection?.disconnect() + session = null + msgIO = null + podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.DISCONNECTED } - bleCommCallbacks.resetConnection() - session = null - msgIO = null } private fun waitForConnection(connectionWaitCond: ConnectionWaitCondition): ConnectionState {