From 6d73080b0eac178ffb9eef52551baaef465d3e67 Mon Sep 17 00:00:00 2001 From: Selcuk Kekec Date: Thu, 3 Nov 2022 21:23:42 +0100 Subject: [PATCH 1/9] 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 8fb0dea2f6b..1049bb98621 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 7df9fe9729d..a2f84ab42df 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 446ccf39ee0..14a0d849043 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 8d09939b117..c13ead91501 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 bbd7b3f6f897d91a4db601152548d098cdbfe280 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Thu, 22 Sep 2022 20:24:27 +0200 Subject: [PATCH 2/9] 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 4423868aca3..e5cc439a0bb 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 9300799bfac5e0287e45ec206137c2a94f78478f Mon Sep 17 00:00:00 2001 From: T-o-b-i-a-s Date: Sat, 5 Nov 2022 12:01:38 +0100 Subject: [PATCH 3/9] Integrate autoISF2.2.7 into AAPS3.1.0.3 --- .../main/assets/OpenAPSSMB/determine-basal.js | 327 +++++++++++++++++- .../nightscout/androidaps/MainActivity.kt | 2 +- .../openAPSSMB/DetermineBasalAdapterSMBJS.kt | 54 ++- app/src/main/res/values/strings.xml | 71 +++- .../main/res/xml/pref_absorption_oref1.xml | 2 +- app/src/main/res/xml/pref_openapssmb.xml | 283 +++++++++++++++ .../iob/iobCobCalculator/GlucoseStatus.kt | 48 ++- .../iobCobCalculator/GlucoseStatusProvider.kt | 244 ++++++++++++- 8 files changed, 1006 insertions(+), 25 deletions(-) diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js index f69317bfa6f..161e2e290da 100644 --- a/app/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -110,7 +110,293 @@ function enable_smb( return false; } +function loop_smb(profile, iob_data) { + if (profile.temptargetSet) { + var target = profile.min_bg; + profile.iob_threshold_percent=101; // effectively disabled; later make it variable + if (profile.iob_threshold_percent/100 < iob_data.iob/profile.max_iob) { + console.error("SMB disabled by full loop logic: iob",iob_data.iob,"is more than", profile.iob_threshold_percent+"% of maxIOB",profile.max_iob); + return "iobTH"; + } else if ( target % 2 == 1 ) { // odd number + console.error("SMB disabled by full loop logic: odd TT"); + return "blocked"; + } else { + console.error("SMB enabled by full loop logic: even TT"); + return "enforced"; // even number + } + } + return "AAPS"; // leave it to standard AAPS +} + +function interpolate(xdata, profile) //, polygon) +{ // V14: interpolate ISF behaviour based on polygons defining nonlinear functions defined by value pairs for ... + // ... <----- delta -------> or <--------------- glucose -------------------> + var polyX = [ 2, 7, 12, 16, 20, 50, 60, 80, 90, 100, 110, 150, 180, 200]; // later, hand it over + var polyY = [0.0, 0.0, 0.4, 0.7, 0.7, -0.5, -0.5, -0.3, -0.2, 0.0, 0.0, 0.5, 0.7, 0.7]; // later, hand it over + var polymax = polyX.length-1; + var step = polyX[0]; + var sVal = polyY[0]; + var stepT= polyX[polymax]; + var sValold = polyY[polymax]; + + var newVal = 1; + var lowVal = 1; + var topVal = 1; + var lowX = 1; + var topX = 1; + var myX = 1; + var lowLabl = step; + + if (step > xdata) { + // extrapolate backwards + stepT = polyX[1]; + sValold = polyY[1]; + lowVal = sVal; + topVal = sValold; + lowX = step; + topX = stepT; + myX = xdata; + newVal = lowVal + (topVal-lowVal)/(topX-lowX)*(myX-lowX); + } else if (stepT < xdata) { + // extrapolate forwards + step = polyX[polymax-1]; + sVal = polyY[polymax-1]; + lowVal = sVal; + topVal = sValold; + lowX = step; + topX = stepT; + myX = xdata; + newVal = lowVal + (topVal-lowVal)/(topX-lowX)*(myX-lowX); + } else { + // interpolate + for (var i=0; i <= polymax; i++) { + step = polyX[i]; + sVal = polyY[i]; + if (step == xdata) { + newVal = sVal; + break; + } else if (step > xdata) { + topVal = sVal; + lowX= lowLabl; + myX = xdata; + topX= step; + newVal = lowVal + (topVal-lowVal)/(topX-lowX)*(myX-lowX); + break; + } + lowVal = sVal; + lowLabl= step; + } + } + if ( xdata>100 ) { newVal = newVal * profile.higher_ISFrange_weight } // higher BG range + else if ( xdata>40 ) { newVal = newVal * profile.lower_ISFrange_weight } // lower BG range, but not delta range + else { newVal = newVal * profile.delta_ISFrange_weight } // delta range + return newVal; +} + +function withinISFlimits(liftISF, minISFReduction, maxISFReduction, sensitivityRatio) +{ // extracted 17.Mar.2022 + if ( liftISF < minISFReduction ) { // mod V14j + console.error("weakest ISF factor", round(liftISF,2), "limited by autoisf_min", minISFReduction); // mod V14j + liftISF = minISFReduction; // mod V14j + } else if ( liftISF > maxISFReduction ) { // mod V14j + console.error("strongest ISF factor", round(liftISF,2), "limited by autoisf_max", maxISFReduction); // mod V14j + liftISF = maxISFReduction; // mod V14j + } + var final_ISF = 1; + if ( liftISF >= 1 ) { final_ISF = Math.max(liftISF, sensitivityRatio); } + if ( liftISF < 1 ) { final_ISF = Math.min(liftISF, sensitivityRatio); } + console.error("final ISF factor is", round(final_ISF,2)); + return final_ISF; +} + +function autoISF(sens, target_bg, profile, glucose_status, meal_data, currentTime, autosens_data, sensitivityRatio) +{ // #### mod 7e: added switch for autoISF ON/OFF + if ( !profile.enable_autoISF ) { + console.error("autoISF disabled in Preferences"); + return sens; + } + // #### mod 7: dynamic ISF strengthening based on duration and width of +/-5% BG band + // #### mod 7b: misuse autosens_min to get the scale factor + // #### mod 7d: use standalone variables for autoISF + // #### mod 7e: enable autoISF via menu + // #### mod 7f: enable autoISF_with_COB via menu + // #### mod 14 : Adapt ISF based on bg and delta + // #### mod 14j: Adapt ISF based on bg acceleration/deceleration + var dura05 = glucose_status.dura_ISF_minutes; // mod 7d + var avg05 = glucose_status.dura_ISF_average; // mod 7d + // mod V14 dated 06.JUN.2021 starts + var maxISFReduction = profile.autoISF_max; // mod 7d + var sens_modified = false; + var pp_ISF = 1; // mod 14f + var delta_ISF = 1; // mod 14f + var acce_ISF = 1; // mod 14j + var bg_off = target_bg+10 - avg05; // move from central BG=100 to target+10 as virtual BG'=100 + + // start of mod V14j: calculate acce_ISF from bg acceleration and adapt ISF accordingly + var fit_corr = glucose_status.parabola_fit_correlation; + var bg_acce = glucose_status.bg_acceleration; + if (glucose_status.parabola_fit_a2 !=0 && fit_corr>=0.9) { + var minmax_delta = - glucose_status.parabola_fit_a1/2/glucose_status.parabola_fit_a2 * 5; // back from 5min block to 1 min + var minmax_value = round(glucose_status.parabola_fit_a0 - minmax_delta*minmax_delta/25*glucose_status.parabola_fit_a2, 1); + minmax_delta = round(minmax_delta, 1) + //if (minmax_delta<0 && bg_acce<0) { + // console.error("Parabolic fit saw maximum of", minmax_value, "about", -minmax_delta, "minutes ago"); + //} else if (minmax_delta<0 && bg_acce>0) { + // console.error("Parabolic fit saw minimum of", minmax_value, "about", -minmax_delta, "minutes ago"); + //} else if (minmax_delta>0 && bg_acce<0) { + if (minmax_delta>0 && bg_acce<0) { + console.error("Parabolic fit extrapolates a maximum of", minmax_value, "in about", minmax_delta, "minutes"); + } else if (minmax_delta>0 && bg_acce>0) { + console.error("Parabolic fit extrapolates a minimum of", minmax_value, "in about", minmax_delta, "minutes"); + } + } + if ( fit_corr<0.9 ) { + console.error("acce_ISF adaptation by-passed as correlation", round(fit_corr,3), "is too low"); + } else { + var fit_share = 10*(fit_corr-0.9); // 0 at correlation 0.9, 1 at 1.00 + var cap_weight = 1; // full contribution above target + var acce_weight = 1; + if ( glucose_status.glucose 0 ) { + if ( bg_acce>1) { cap_weight = 0.5; } // halve the effect below target + acce_weight = profile.bgBrake_ISF_weight; + + } else if ( bg_acce < 0 ) { + acce_weight = profile.bgAccel_ISF_weight; + } + } else { // above target acce goes away from target + if ( bg_acce < 0 ) { + acce_weight = profile.bgBrake_ISF_weight; + } else if ( bg_acce > 0 ) { + + acce_weight = profile.bgAccel_ISF_weight; + } + } + acce_ISF = 1 + bg_acce * cap_weight * acce_weight * fit_share; + console.error("acce_ISF adaptation is", round(acce_ISF,2)); + if ( acce_ISF != 1 ) { + sens_modified = true; + } + } + // end of mod V14j code block + + var bg_ISF = 1 + interpolate(100-bg_off, profile); + console.error("bg_ISF adaptation is", round(bg_ISF,2)); + var liftISF = 1; + var final_ISF = 1; + if (bg_ISF<1) { + liftISF = Math.min(bg_ISF, acce_ISF); + if ( acce_ISF>1 ) { // mod V14j + liftISF = bg_ISF * acce_ISF; // mod V14j: bg_ISF could become > 1 now + console.error("bg_ISF adaptation lifted to", round(liftISF,2), "as bg accelerates already"); // mod V14j + } // mod V14j + final_ISF = withinISFlimits(liftISF, profile.autoISF_min, maxISFReduction, sensitivityRatio); + //if ( liftISF < profile.autoisf_min ) { // mod V14j + // console.error("final ISF factor", round(liftISF,2), "limited by autoisf_min", profile.autoisf_min); // mod V14j + // liftISF = profile.autoisf_min; // mod V14j + //} else if ( bg_ISF > maxISFReduction ) { // mod V14j: not possible here + // console.error("bg_ISF adaptation", round(bg_ISF,2), "limited by autoisf_max", maxISFReduction); // mod V14j + // bg_ISF = maxISFReduction; // mod V14j + //} // mod V14j + return Math.min(720, round(profile.sens / final_ISF, 1)); // mod V14j: observe ISF maximum of 720(?) + } else if ( bg_ISF > 1 ) { + sens_modified = true; + } + + var bg_delta = glucose_status.delta; + if (profile.enable_pp_ISF_always || profile.pp_ISF_hours >= (currentTime - meal_data.lastCarbTime) / 1000/3600) { // corrected logic on 17.Sep.2021 + deltaType = 'pp' + } else { + deltaType = 'delta' + } + if (bg_off > 0) { + console.error(deltaType+"_ISF adaptation by-passed as average glucose < "+target_bg+"+10"); + } else if (glucose_status.short_avgdelta<0) { + console.error(deltaType+"_ISF adaptation by-passed as no rise or too short lived"); + } else if (deltaType == 'pp') { + pp_ISF = 1 + Math.max(0, bg_delta * profile.pp_ISF_weight); + console.error("pp_ISF adaptation is", round(pp_ISF,2)); + if (pp_ISF != 1) { + sens_modified = true; + } + + } else { + delta_ISF = interpolate(bg_delta, profile); + // mod V14d: halve the effect below target_bg+30 + if ( bg_off > -20 ) { + delta_ISF = 0.5 * delta_ISF; + } + delta_ISF = 1 + delta_ISF; + console.error("delta_ISF adaptation is", round(delta_ISF,2)); + + if (delta_ISF != 1) { + sens_modified = true; + } + } + + var dura_ISF = 1 + var weightISF = profile.dura_ISF_weight; // mod 7d: specify factor directly; use factor 0 to shut autoISF OFF + if (meal_data.mealCOB>0 && !profile.enable_dura_ISF_with_COB) { + console.error("dura_ISF by-passed; preferences disabled mealCOB of "+round(meal_data.mealCOB,1)); // mod 7f + } else if (dura05<10) { + console.error("dura_ISF by-passed; bg is only "+dura05+"m at level "+avg05); + } else if (avg05 <= target_bg) { + console.error("dura_ISF by-passed; avg. glucose", avg05, "below target", target_bg); + } else { + // # fight the resistance at high levels + var dura05_weight = dura05 / 60; + var avg05_weight = weightISF / target_bg; // mod gz7b: provide access from AAPS + dura_ISF += dura05_weight*avg05_weight*(avg05-target_bg); + sens_modified = true; + console.error("dura_ISF adaptation is", round(dura_ISF,2), "because ISF", round(sens,1), "did not do it for", round(dura05,1),"m"); + } + if ( sens_modified ) { + liftISF = Math.max(dura_ISF, bg_ISF, delta_ISF, acce_ISF, pp_ISF); + if ( acce_ISF < 1 ) { // mod V14j: 13.JAN.2022 brakes on for otherwise stronger or stable ISF + console.error("strongest ISF factor", round(liftISF,2), "weakened to", round(liftISF*acce_ISF,2), "as bg decelerates already"); // mod V14j + liftISF = liftISF * acce_ISF; // mod V14j: brakes on for otherwise stronger or stable ISF + } // mod V14j: brakes on for otherwise stronger or stable ISF + final_ISF = withinISFlimits(liftISF, profile.autoISF_min, maxISFReduction, sensitivityRatio); + //if ( liftISF < profile.autoisf_min ) { // mod V14j: below minimum? + // console.error("final ISF factor", round(liftISF,2), "limited by autoisf_min", profile.autoisf_min); // mod V14j + // liftISF = profile.autoisf_min; // mod V14j + //} else if ( liftISF > maxISFReduction ) { // mod V14j + // console.error("final ISF factor", round(liftISF,2), "limited by autoisf_max", maxISFReduction); // mod V14j + // liftISF = maxISFReduction; // mod V14j + // //sens = round(profile.sens / Math.max(maxISFReduction, sensitivityRatio, 1); + //} // mod V14j + //if ( liftISF >= 1 ) { return round(profile.sens / Math.max(liftISF, sensitivityRatio), 1); } + //if ( liftISF < 1 ) { return round(profile.sens / Math.min(liftISF, sensitivityRatio), 1); } + return round(profile.sens / final_ISF, 1); + } + return sens; // mod V14j: nothing changed +} + +function determine_varSMBratio(profile, bg, target_bg) +{ // mod 12: let SMB delivery ratio increase f#rom min to max depending on how much bg exceeds target + if ( typeof profile.smb_delivery_ratio_bg_range === 'undefined' || profile.smb_delivery_ratio_bg_range === 0 ) { + // not yet upgraded to this version or deactivated in SMB extended menu + console.error('SMB delivery ratio set to fixed value', profile.smb_delivery_ratio); + return profile.smb_delivery_ratio; + } + var lower_SMB = Math.min(profile.smb_delivery_ratio_min, profile.smb_delivery_ratio_max); + if (bg <= target_bg) { + console.error('SMB delivery ratio limited by minimum value', lower_SMB); + return lower_SMB; + } + var higher_SMB = Math.max(profile.smb_delivery_ratio_min, profile.smb_delivery_ratio_max); + var higher_bg = target_bg + profile.smb_delivery_ratio_bg_range; + if (bg >= higher_bg) { + console.error('SMB delivery ratio limited by maximum value', higher_SMB); + return higher_SMB; + } + var new_SMB = lower_SMB + (higher_SMB - lower_SMB)*(bg-target_bg) / profile.smb_delivery_ratio_bg_range; + console.error('SMB delivery ratio set to interpolated value', new_SMB); + return new_SMB; +} + var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, autosens_data, meal_data, tempBasalFunctions, microBolusAllowed, reservoir_data, currentTime, isSaveCgmSource) { + var rT = {}; //short for requestedTemp var deliverAt = new Date(); @@ -173,6 +459,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ return rT; } } + // console.error('Meal age is:', (currentTime - meal_data.lastCarbTime) / 1000/3600, 'hours'); var max_iob = profile.max_iob; // maximum amount of non-bolus IOB OpenAPS will ever deliver @@ -288,7 +575,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ //console.log(" (autosens ratio "+sensitivityRatio+")"); } console.error("CR:",profile.carb_ratio); - + sens = autoISF(sens, target_bg, profile, glucose_status, meal_data, currentTime, autosens_data, sensitivityRatio); // compare currenttemp to iob_data.lastTemp and cancel temp if they don't match var lastTempAge; if (typeof iob_data.lastTemp !== 'undefined' ) { @@ -397,9 +684,9 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ return rT; } - // min_bg of 90 -> threshold of 65, 100 -> 70 110 -> 75, and 130 -> 85 - var threshold = min_bg - 0.5*(min_bg-40); - + var threshold_ratio = 0.5; + var threshold = threshold_ratio * min_bg + 20; + threshold = round(threshold); //console.error(reservoir_data); rT = { @@ -427,13 +714,20 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ ZTpredBGs.push(bg); UAMpredBGs.push(bg); - var enableSMB = enable_smb( + var loop_wants_smb = loop_smb(profile, iob_data); + var enableSMB = false; + var loop_wanted_smb = loop_wants_smb; + if (microBolusAllowed && loop_wanted_smb != "AAPS") { + if ( loop_wanted_smb == "enforced" ) { // otherwise FL switched SMB off + enableSMB = true; + } + } else { enableSMB = enable_smb( profile, microBolusAllowed, meal_data, target_bg - ); - + ); + } // enable UAM (if enabled in preferences) var enableUAM=(profile.enableUAM); @@ -1054,26 +1348,35 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ if (microBolusAllowed && enableSMB && bg > threshold) { // never bolus more than maxSMBBasalMinutes worth of basal var mealInsulinReq = round( meal_data.mealCOB / profile.carb_ratio ,3); + // mod 10: make the irregular mutiplier a user input + var smb_max_range = profile.smb_max_range_extension; + //if (smb_max_range > 1) { + // console.error("SMB max range extended from default by factor", smb_max_range) + //} if (typeof profile.maxSMBBasalMinutes === 'undefined' ) { - var maxBolus = round( profile.current_basal * 30 / 60 ,1); + var maxBolus = round(smb_max_range * profile.current_basal * 30 / 60 ,1); console.error("profile.maxSMBBasalMinutes undefined: defaulting to 30m"); // if IOB covers more than COB, limit maxBolus to 30m of basal } else if ( iob_data.iob > mealInsulinReq && iob_data.iob > 0 ) { console.error("IOB",iob_data.iob,"> COB",meal_data.mealCOB+"; mealInsulinReq =",mealInsulinReq); if (profile.maxUAMSMBBasalMinutes) { console.error("profile.maxUAMSMBBasalMinutes:",profile.maxUAMSMBBasalMinutes,"profile.current_basal:",profile.current_basal); - maxBolus = round( profile.current_basal * profile.maxUAMSMBBasalMinutes / 60 ,1); + maxBolus = round(smb_max_range * profile.current_basal * profile.maxUAMSMBBasalMinutes / 60 ,1); } else { console.error("profile.maxUAMSMBBasalMinutes undefined: defaulting to 30m"); - maxBolus = round( profile.current_basal * 30 / 60 ,1); + maxBolus = round(smb_max_range * profile.current_basal * 30 / 60 ,1); } } else { console.error("profile.maxSMBBasalMinutes:",profile.maxSMBBasalMinutes,"profile.current_basal:",profile.current_basal); - maxBolus = round( profile.current_basal * profile.maxSMBBasalMinutes / 60 ,1); + maxBolus = round(smb_max_range * profile.current_basal * profile.maxSMBBasalMinutes / 60 ,1); } // bolus 1/2 the insulinReq, up to maxBolus, rounding down to nearest bolus increment var roundSMBTo = 1 / profile.bolus_increment; - var microBolus = Math.floor(Math.min(insulinReq/2,maxBolus)*roundSMBTo)/roundSMBTo; + // mod 10: make the share of InsulinReq a user input + // mod 12: make the share of InsulinReq a user configurable interpolation range + var smb_ratio = determine_varSMBratio(profile, bg, target_bg); + var microBolus = Math.min(insulinReq*smb_ratio, maxBolus); + microBolus = Math.floor(microBolus*roundSMBTo)/roundSMBTo; // calculate a long enough zero temp to eventually correct back up to target var smbTarget = target_bg; worstCaseInsulinReq = (smbTarget - (naive_eventualBG + minIOBPredBG)/2 ) / sens; diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.kt b/app/src/main/java/info/nightscout/androidaps/MainActivity.kt index 241da0bed93..a81af251880 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.kt @@ -343,7 +343,7 @@ class MainActivity : NoSplashAppCompatActivity() { val messageSpanned = SpannableString(message) Linkify.addLinks(messageSpanned, Linkify.WEB_URLS) MaterialAlertDialogBuilder(this, R.style.DialogTheme) - .setTitle(rh.gs(R.string.app_name) + " " + BuildConfig.VERSION) + .setTitle(rh.gs(R.string.app_name) + " " + BuildConfig.VERSION + "+autoISF2.2.7") .setIcon(iconsProvider.getIcon()) .setMessage(messageSpanned) .setPositiveButton(rh.gs(R.string.ok), null) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt index c23f14d54a8..fad213dbec0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt @@ -206,8 +206,13 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: this.profile.put("enableSMB_with_COB", smbEnabled && sp.getBoolean(R.string.key_enableSMB_with_COB, false)) this.profile.put("enableSMB_with_temptarget", smbEnabled && sp.getBoolean(R.string.key_enableSMB_with_temptarget, false)) this.profile.put("allowSMB_with_high_temptarget", smbEnabled && sp.getBoolean(R.string.key_allowSMB_with_high_temptarget, false)) - this.profile.put("enableSMB_always", smbEnabled && sp.getBoolean(R.string.key_enableSMB_always, false) && advancedFiltering) - this.profile.put("enableSMB_after_carbs", smbEnabled && sp.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering) + + // mod 13: allow SMBalways and enableSMB_after_carbs if selected in preferences + // this.profile.put("enableSMB_always", smbEnabled && sp.getBoolean(R.string.key_enableSMB_always, false) && advancedFiltering) + // this.profile.put("enableSMB_after_carbs", smbEnabled && sp.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering) + this.profile.put("enableSMB_always", smbEnabled && sp.getBoolean(R.string.key_enableSMB_always, false)) // && advancedFiltering) + this.profile.put("enableSMB_after_carbs", smbEnabled && sp.getBoolean(R.string.key_enableSMB_after_carbs, false)) // && advancedFiltering) + this.profile.put("maxSMBBasalMinutes", sp.getInt(R.string.key_smbmaxminutes, SMBDefaults.maxSMBBasalMinutes)) this.profile.put("maxUAMSMBBasalMinutes", sp.getInt(R.string.key_uamsmbmaxminutes, SMBDefaults.maxUAMSMBBasalMinutes)) //set the min SMB amount to be the amount set by the pump. @@ -216,6 +221,31 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: this.profile.put("current_basal", basalRate) this.profile.put("temptargetSet", tempTargetSet) this.profile.put("autosens_max", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_autosens_max, "1.2"))) + + // mod 7e: can I add use autoisf here? + this.profile.put("enable_autoISF", sp.getBoolean(R.string.key_enable_autoISF, false)) + // mod 7f: can I add use autoisf with COB here? + this.profile.put("autoISF_max", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_autoISF_max, "1.0"))) + this.profile.put("autoISF_min", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_autoISF_min, "1.0"))) + this.profile.put("bgAccel_ISF_weight", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_bgAccel_ISF_weight, "0.0"))) + this.profile.put("bgBrake_ISF_weight", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_bgBrake_ISF_weight, "0.0"))) + // mod 14f: for pp_ISF without meal + this.profile.put("enable_pp_ISF_always", sp.getBoolean(R.string.key_enable_postprandial_ISF_always, false)) + this.profile.put("pp_ISF_hours", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_pp_ISF_hours, "3.0"))) + this.profile.put("pp_ISF_weight", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_pp_ISF_weight, "0.0"))) + this.profile.put("delta_ISFrange_weight", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_delta_ISFrange_weight, "0.0"))) + this.profile.put("lower_ISFrange_weight", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_lower_ISFrange_weight, "0.0"))) + this.profile.put("higher_ISFrange_weight", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_higher_ISFrange_weight, "0.0"))) + this.profile.put("enable_dura_ISF_with_COB", sp.getBoolean(R.string.key_enable_dura_ISF_with_COB, false)) + // mod 7d: can I add autosens_min here? + this.profile.put("dura_ISF_weight", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_dura_ISF_weight, "0.0"))) + // mod 10: include SMB manipulations to be accessible in determine-basal + this.profile.put("smb_delivery_ratio", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_smb_delivery_ratio, "0.5"))) + this.profile.put("smb_delivery_ratio_min", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_smb_delivery_ratio_min, "0.5"))) + this.profile.put("smb_delivery_ratio_max", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_smb_delivery_ratio_max, "0.5"))) + this.profile.put("smb_delivery_ratio_bg_range", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_smb_delivery_ratio_bg_range, "0"))) + this.profile.put("smb_max_range_extension", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_smb_max_range_extension, "1.0"))) + if (profileFunction.getUnits() == GlucoseUnit.MMOL) { this.profile.put("out_units", "mmol/L") } @@ -238,6 +268,24 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: mGlucoseStatus.put("short_avgdelta", glucoseStatus.shortAvgDelta) mGlucoseStatus.put("long_avgdelta", glucoseStatus.longAvgDelta) mGlucoseStatus.put("date", glucoseStatus.date) + + // mod 7: append 2 variables for 5% range + mGlucoseStatus.put("dura_ISF_minutes", glucoseStatus.dura_ISF_minutes) + mGlucoseStatus.put("dura_ISF_average", glucoseStatus.dura_ISF_average) + // mod 8: append variables for linear fit + mGlucoseStatus.put("slope05", glucoseStatus.slope05) + mGlucoseStatus.put("slope15", glucoseStatus.slope15) + mGlucoseStatus.put("slope40", glucoseStatus.slope40) + // mod 14g: append variables for quadratic fit + mGlucoseStatus.put("parabola_fit_correlation", glucoseStatus.r_squ) + mGlucoseStatus.put("parabola_fit_minutes", glucoseStatus.dura_p) + mGlucoseStatus.put("parabola_fit_last_delta", glucoseStatus.delta_pl) + mGlucoseStatus.put("parabola_fit_next_delta", glucoseStatus.delta_pn) + mGlucoseStatus.put("parabola_fit_a0", glucoseStatus.a_0) + mGlucoseStatus.put("parabola_fit_a1", glucoseStatus.a_1) + mGlucoseStatus.put("parabola_fit_a2", glucoseStatus.a_2) + mGlucoseStatus.put("bg_acceleration", glucoseStatus.bg_acceleration) + this.mealData.put("carbs", mealData.carbs) this.mealData.put("mealCOB", mealData.mealCOB) this.mealData.put("slopeFromMaxDeviation", mealData.slopeFromMaxDeviation) @@ -252,7 +300,7 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: this.microBolusAllowed = microBolusAllowed smbAlwaysAllowed = advancedFiltering currentTime = now - saveCgmSource = isSaveCgmSource + saveCgmSource = true // for non_Libre use; was: isSaveCgmSource } private fun makeParam(jsonObject: JSONObject?, rhino: Context, scope: Scriptable): Any { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9704987c132..c4cf602bcc9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -313,7 +313,22 @@ Autosens data Script debug Use Autosens feature - Refresh from NS + Refresh events from NS + autoISF settings + autoISF allows to adapt the insulin sensitivity factor (ISF) in various scenarios of glucose behaviour + acce_ISF settings + acce_ISF settings: adjust ISF for accelerating/decelerating blood glucose + bg_ISF settings + bg_ISF settings: ISF depending on blood glucose level according to a predefined polynom + pp_ISF settings + pp_ISF settings: ISF for postprandial (after meal) glucose rise + delta_ISF settings + delta_ISF settings: ISF adaptations for positive delta, i.e. rising blood glucose + dura_ISF settings + dura_ISF settings: ISF for blood glucose plateau above target + smb delivery settings + smb delivery settings: Set of options to increase the actual SMB size which can be delivered in situations where ISF was strengthened, i.e. fairly low ISF figure.\n\nUSE WITH CAUTION + Use autoISF feature Delete treatments in the future ACT CONF @@ -333,8 +348,36 @@ Profile Default value: 3 This is a key OpenAPS safety cap. What this does is limit your basals to be 3x (in this people) your biggest basal rate. You likely will not need to change this, but you should be aware that’s what is discussed about “3x max daily; 4x current” for safety caps. Default value: 4 This is the other half of the key OpenAPS safety caps, and the other half of “3x max daily; 4x current” of the safety caps. This means your basal, regardless of max basal set on your pump, cannot be any higher than this number times the current level of your basal. This is to prevent people from getting into dangerous territory by setting excessively high max basals before understanding how the algorithm works. Again, the default is 4x; most people will never need to adjust this and are instead more likely to need to adjust other settings if they feel like they are “running into” this safety cap. + Default value: 0.5 This is another key OpenAPS safety cap, and specifies what share of the total insulin required can be delivered as SMB. This is to prevent people from getting into dangerous territory by setting SMB requests from the caregivers phone at the same time. Increase this experimental value slowly and with caution. + Default value: 0.5 This is the lower end of a linearly increasing ratio rather than the fix value above. + Default value: 0.5 This is the upper end of a linearly increasing ratio rather than the fix value above. + Default value: 0. The linearly increasing SMB delivery ratio is mapped to the glucose range [target_bg, target_bg+bg_range].\nAt target_bg the SMB ratio is smb_delivery_ratio_min, at target_bg+bg_range it is smb_delivery_ratio_max\nWith 0 the linearly increasing SMB ratio is disabled and the fixed smb_delivery_ratio is used. + Default value: 1 This is another key OpenAPS safety cap, and specifies by what factor you can exceed the regular 120 maxSMB/maxUAM minutes. Increase this experimental value slowly and with caution. + autosens_max Default value: 1.2\nThis is a multiplier cap for autosens (and soon autotune) to set a 20%% max limit on how high the autosens ratio can be, which in turn determines how high autosens can adjust basals, how low it can adjust ISF, and how low it can set the BG target. Default value: 0.7\nThe other side of the autosens safety limits, putting a cap on how low autosens can adjust basals, and how high it can adjust ISF and BG targets. + autoISF_max + autoISF_min + Default value: 1.2\nThis is a multiplier cap for autoISF to set a limit on how high the autoISF ratio can be, which in turn determines how low it can adjust ISF. + Default value: 1.0\nThis is a multiplier cap for autoISF to set a limit on how low the autoISF ratio can be, which in turn determines how high it can adjust ISF. + dura_ISF_weight + Default value: 0.0\nThis is the rate at which autoISF grows per hour assuming bg is twice the target. With a value of 1.0 it will have reduced ISF to 50% after 1 hour of bg at twice the target.\n\nWith 0.0 the effect of autoISF is effectively disabled. + lower_ISFrange_weight + Default value: 0.0\nThis is the weight applied to the polygon which adapats ISF if glucose is below target. \n\nWith 0.0 the effect is effectively disabled. + higher_ISFrange_weight + Default value: 0.0\nThis is the weight applied to the polygon which adapats ISF if glucose is above target. \n\nWith 0.0 the effect is effectively disabled. + delta_ISFrange_weight + Default value: 0.0\nThis is the weight applied to the polygon which adapats ISF for higher deltas. \n\nWith 0.0 the effect is effectively disabled. + pp_ISF_weight + Default value: 0.0\nThis is the weight applied to the linear slope while glucose rises and which adapts ISF. \n\nWith 0.0 this contribution is effectively disabled. + pp_ISF_hours + Default value: 3\nThis is the duration in hours how long after a meal the effect will be active. \n\nAAPS will delete carb timing after 10 hours latest no matter what you enter. + enable_postprandial_ISF_always + Enable the postprandial ISF adaptation all the time regardless of when the last meal was taken. + bgAccel_ISF_weight + Default value: 0.0\nThis is the weight applied while glucose accelerates and which strengthens ISF. \n\nWith 0.0 this contribution is effectively disabled. + bgBrake_ISF_weight + Default value: 0.0\nThis is the weight applied while glucose decelerates and which weakens ISF. \n\nWith 0.0 this contribution is effectively disabled. Autosens adjust targets, too Default value: true\nThis is used to allow autosens to adjust BG targets, in addition to ISF and basals. Default value: 2\nBolus snooze is enacted after you do a meal bolus, so the loop won’t counteract with low temps when you’ve just eaten. The example here and default is 2; so a 3 hour DIA means that bolus snooze will be gradually phased out over 1.5 hours (3DIA/2). @@ -524,6 +567,7 @@ Adjustment factor for DynamicISF. Set more than 100% for more aggressive correction doses, and less than 100% for less aggressive corrections. use_smb use_uam + openapsama_enable_autoISF smb_enable_carbs_suggestions_threshold Enable UAM Enable SMB @@ -612,6 +656,10 @@ Maximal IOB set properly BG available from selected source Extended bolus delivery error + enable_dura_ISF_with_COB + Enable dura_ISF with COB + Enable dura_ISF when there is COB active + Enable pp_ISF postprandial all day enableSMB_always enableSMB_with_COB enableSMB_with_temptarget @@ -696,6 +744,11 @@ openapsma_max_basal openapsama_current_basal_safety_multiplier openapsama_max_daily_safety_multiplier + openapsama_smb_delivery_ratio + openapsama_smb_delivery_ratio_min + openapsama_smb_delivery_ratio_max + openapsama_smb_delivery_ratio_bg_range + openapsama_smb_max_range_extension max basal multiplier max daily basal multiplier openapsma_max_iob @@ -709,6 +762,7 @@ max value in preferences hard limit openapsama_useautosens + openapsama_enable_autoISF Read status failed Record pump site change Record insulin cartridge change @@ -753,9 +807,24 @@ always_use_shortavg Max autosens ratio Min autosens ratio + Max autoISF ratio\n(autoISF_max) + Min autoISF ratio\n(autoISF_min) + lower ISF-range weight\n(lower_ISFrange_weight) + higher ISF-range weight\n(higher_ISFrange_weight) + delta ISF-range weight\n(delta_ISFrange_weight) + postprandial ISF weight\n(pp_ISF_weight) + postprandial ISF time window\n(pp_ISF_hours) + Weight while BG accelerates\n(bgAccel_ISF_weight) + Weight while BG decelerates\n(bgBrake_ISF_weight) + dura_ISF weight\n(dura_ISF_weight) Bolus snooze dia divisor Max daily safety multiplier Current basal safety multiplier + fixed SMB delivery ratio\n(smb_delivery_ratio) + variable SMB delivery ratio, lower end\n(smb_delivery_ratio_min) + variable SMB delivery ratio, upper end\n(smb_delivery_ratio_max) + variable SMB delivery ratio, mapped glucose range\n(smb_delivery_ratio_bg_range) + SMB/UAM max range extension\n(smb_max_range_extension) n/a virtualpump_uploadstatus Virtual Pump Type diff --git a/app/src/main/res/xml/pref_absorption_oref1.xml b/app/src/main/res/xml/pref_absorption_oref1.xml index 62c93f1ed9f..84e0ee5e047 100644 --- a/app/src/main/res/xml/pref_absorption_oref1.xml +++ b/app/src/main/res/xml/pref_absorption_oref1.xml @@ -9,7 +9,7 @@ android:title="@string/absorptionsettings_title" app:initialExpandedChildrenCount="0"> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/GlucoseStatus.kt b/core/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/GlucoseStatus.kt index 9173bf5d594..029586777ff 100644 --- a/core/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/GlucoseStatus.kt +++ b/core/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/GlucoseStatus.kt @@ -9,14 +9,41 @@ data class GlucoseStatus( val delta: Double = 0.0, val shortAvgDelta: Double = 0.0, val longAvgDelta: Double = 0.0, - val date: Long = 0L + val date: Long = 0L, + // mod 7: append 2 variables for 5% range + val dura_ISF_minutes: Double = 0.0, + val dura_ISF_average: Double = 0.0, + // mod 8: append 3 variables for deltas based on regression analysis + val slope05: Double = 0.0, // wait for longer history + val slope15: Double = 0.0, // wait for longer history + val slope40: Double = 0.0, // wait for longer history + // mod 14f: append results from best fitting parabola + val dura_p: Double = 0.0, + val delta_pl: Double = 0.0, + val delta_pn: Double = 0.0, + val r_squ: Double = 0.0, + val bg_acceleration: Double = 0.0, + val a_0: Double = 0.0, + val a_1: Double = 0.0, + val a_2: Double = 0.0, + val pp_debug: String = "; debug:" ) { fun log(): String = "Glucose: " + DecimalFormatter.to0Decimal(glucose) + " mg/dl " + "Noise: " + DecimalFormatter.to0Decimal(noise) + " " + - "Delta: " + DecimalFormatter.to0Decimal(delta) + " mg/dl" + + "Delta: " + DecimalFormatter.to0Decimal(delta) + " mg/dl " + "Short avg. delta: " + " " + DecimalFormatter.to2Decimal(shortAvgDelta) + " mg/dl " + - "Long avg. delta: " + DecimalFormatter.to2Decimal(longAvgDelta) + " mg/dl" + "Long avg. delta: " + DecimalFormatter.to2Decimal(longAvgDelta) + " mg/dl " + + "Range length: " + DecimalFormatter.to0Decimal(dura_ISF_minutes) + " min " + + "Range average: " + DecimalFormatter.to2Decimal(dura_ISF_average) + " mg/dl; " + + "5 min fit delta: " + DecimalFormatter.to2Decimal(slope05) + " mg/dl; " + + "15 min fit delta: " + DecimalFormatter.to2Decimal(slope15) + " mg/dl; " + + "40 min fit delta: " + DecimalFormatter.to2Decimal(slope40) + " mg/dl; " + + "parabola length: " + DecimalFormatter.to2Decimal(dura_p) + " min; " + + "parabola last delta: " + DecimalFormatter.to2Decimal(delta_pl) + " mg/dl; " + + "parabola next delta: " + DecimalFormatter.to2Decimal(delta_pn) + " mg/dl; " + + "bg_acceleration: " + DecimalFormatter.to2Decimal(bg_acceleration) + " mg/dl/(25m^2); " + + "fit correlation: " + r_squ + pp_debug } fun GlucoseStatus.asRounded() = copy( @@ -24,5 +51,18 @@ fun GlucoseStatus.asRounded() = copy( noise = Round.roundTo(noise, 0.01), delta = Round.roundTo(delta, 0.01), shortAvgDelta = Round.roundTo(shortAvgDelta, 0.01), - longAvgDelta = Round.roundTo(longAvgDelta, 0.01) + longAvgDelta = Round.roundTo(longAvgDelta, 0.01), + dura_ISF_minutes = Round.roundTo(dura_ISF_minutes, 0.1), + dura_ISF_average = Round.roundTo(dura_ISF_average, 0.1), + slope05 = Round.roundTo(slope05, 0.01), + slope15 = Round.roundTo(slope15, 0.01), + slope40 = Round.roundTo(slope40, 0.01), + dura_p = Round.roundTo(dura_p, 0.1), + delta_pl = Round.roundTo(delta_pl, 0.01), + delta_pn = Round.roundTo(delta_pn, 0.01), + bg_acceleration = Round.roundTo(bg_acceleration, 0.01), + r_squ = Round.roundTo(r_squ, 0.0001), + a_0 = Round.roundTo(a_0, 0.1), + a_1 = Round.roundTo(a_1, 0.01), + a_2 = Round.roundTo(a_2, 0.01) ) \ No newline at end of file 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 f722383f5f5..f8c51575ae1 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 @@ -2,12 +2,17 @@ package info.nightscout.androidaps.plugins.iob.iobCobCalculator import dagger.Reusable import info.nightscout.androidaps.interfaces.IobCobCalculator +import info.nightscout.androidaps.utils.DateUtil import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag -import info.nightscout.androidaps.utils.DateUtil +import org.spongycastle.asn1.x500.style.RFC4519Style import java.util.* import javax.inject.Inject import kotlin.math.roundToLong +import org.spongycastle.asn1.x500.style.RFC4519Style.c + + + @Reusable class GlucoseStatusProvider @Inject constructor( @@ -41,7 +46,23 @@ class GlucoseStatusProvider @Inject constructor( delta = 0.0, shortAvgDelta = 0.0, longAvgDelta = 0.0, - date = nowDate + date = nowDate, + // mod 7: append 2 variables for 5% range + dura_ISF_minutes = 0.0, + dura_ISF_average = now.value, + // mod 8: append 3 variables for deltas based on regression analysis + slope05 = 0.0, // wait for longer history + slope15 = 0.0, // wait for longer history + slope40 = 0.0, // wait for longer history + // mod 14f: append results from best fitting parabola + dura_p = 0.0, + delta_pl = 0.0, + delta_pn = 0.0, + bg_acceleration = 0.0, + a_0 = 0.0, + a_1 = 0.0, + a_2 = 0.0, + r_squ = 0.0 ).asRounded() } val nowValueList = ArrayList() @@ -90,6 +111,209 @@ class GlucoseStatusProvider @Inject constructor( } else { average(lastDeltas) } + + // mod 7: calculate 2 variables for 5% range + // initially just test the handling of arguments + // status.dura05 = 11d; + // status.avg05 = 47.11d; + // mod 7a: now do the real maths + val bw = 0.05 // used for Eversense; may be lower for Dexcom + var sumBG: Double = now.value + var oldavg: Double = now.value + var minutesdur = Math.round(0L / (1000.0 * 60)) + for (i in 1 until sizeRecords) { + val then = data[i] + val thenDate: Long = then.timestamp + // mod 7c: stop the series if there was a CGM gap greater than 13 minutes, i.e. 2 regular readings + if (Math.round((nowDate - thenDate) / (1000.0 * 60)) - minutesdur > 13) { + break + } + if (then.value > oldavg * (1 - bw) && then.value < oldavg * (1 + bw)) { + sumBG += then.value + oldavg = sumBG / (i + 1) + minutesdur = Math.round((nowDate - thenDate) / (1000.0 * 60)) + } else { + break + } + } + var autoISFAverage = oldavg + var autoISFDuration = minutesdur.toDouble() + + // mod 8: calculate 3 variables for deltas based on linear regression + // initially just test the handling of arguments + var slope05 = 1.05 + var slope15 = 1.15 + var slope40 = 1.40 + + // mod 8a: now do the real maths based on + // http://www.carl-engler-schule.de/culm/culm/culm2/th_messdaten/mdv2/auszug_ausgleichsgerade.pdf + sumBG = 0.0 // y + var sumt = 0L // x + var sumBG2 = 0.0 // y^2 + var sumt2 = 0L // x^2 + var sumxy = 0.0 // x*y + //double a; + var b: Double // y = a + b * x + var level = 7.5 + var minutesL: Long + // here, longer deltas include all values from 0 up the related limit + for (i in 0 until sizeRecords) { + val then = data[i] + val thenDate = then.timestamp + minutesL = (nowDate - thenDate) / (1000L * 60) + // watch out: the scan goes backwards in time, so delta has wrong sign + if(i * sumt2 == sumt * sumt) { + b = 0.0 + } + else { + b = (i * sumxy - sumt * sumBG) / (i * sumt2 - sumt * sumt) + } + if (minutesL > level && level == 7.5) { + slope05 = -b * 5 + level = 17.5 + } + if (minutesL > level && level == 17.5) { + slope15 = -b * 5 + level = 42.5 + } + if (minutesL > level && level == 42.5) { + slope40 = -b * 5 + break + } + sumt += minutesL + sumt2 += minutesL * minutesL + sumBG += then.value + sumBG2 += then.value * then.value + sumxy += then.value * minutesL + } + + // mod 14f: calculate best parabola and determine delta by extending it 5 minutes into the future + // nach https://www.codeproject.com/Articles/63170/Least-Squares-Regression-for-Quadratic-Curve-Fitti + // + // y = a2*x^2 + a1*x + a0 or + // y = a*x^2 + b*x + c respectively + + // initially just test the handling of arguments + var ppDebug = "" + var bestA = 0.0 + var bestB = 0.0 + var bestC = 0.0 + var duraP = 0.0 + var deltaPl = 0.0 + var deltaPn = 0.0 + var bgAcceleration = 0.0 + var corrMax = 0.0 + var a0 = 0.0 + var a1 = 0.0 + var a2 = 0.0 + + //if (sizeRecords <= 3) { // last 3 points make a trivial parabola + // duraP = 0.0 + // deltaPl = 0.0 + // deltaPn = 0.0 + // bgAcceleration = 0.0 + // corrMax = 0.0 + // a0 = 0.0 + // a1 = 0.0 + // a2 = 0.0 + //} else { + if (sizeRecords > 3) { + //double corrMin = 0.90; // go backwards until the correlation coefficient goes below + var sy = 0.0 // y + var sx = 0.0 // x + var sx2 = 0.0 // x^2 + var sx3 = 0.0 // x^3 + var sx4 = 0.0 // x^4 + var sxy = 0.0 // x*y + var sx2y = 0.0 // x^2*y + // corrMax = 0.0 + val iframe = data[0] + val time0: Long = iframe.timestamp + var tiLast = 0.0 + //# for best numerical accurarcy time and bg must be of same order of magnitude + val scaleTime = 300.0 //# in 5m; values are 0, -1, -2, -3, -4, ... + val scaleBg = 50.0 //# TIR range is now 1.4 - 3.6 + + for (i in 0 until sizeRecords) { + val then = data[i] + val thenDate = then.timestamp + val ti = (thenDate - time0) / 1000.0 / scaleTime + if (-ti * scaleTime > 47 * 60 ) { // skip records older than 47.5 minutes + break + } else if (ti < tiLast - 7.5 * 60 / scaleTime) { // stop scan if a CGM gap > 7.5 minutes is detected + if (i < 3) { // history too short for fit + duraP = -tiLast / 60.0 + deltaPl = 0.0 + deltaPn = 0.0 + bgAcceleration = 0.0 + corrMax = 0.0 + a0 = 0.0 + a1 = 0.0 + a2 = 0.0 + } + break + } + tiLast = ti + val bg = then.value / scaleBg + sx += ti + sx2 += Math.pow(ti, 2.0) + sx3 += Math.pow(ti, 3.0) + sx4 += Math.pow(ti, 4.0) + sy += bg + sxy += ti * bg + sx2y += Math.pow(ti, 2.0) * bg + val n = i + 1 + var D = 0.0 + var Da = 0.0 + var Db = 0.0 + var Dc = 0.0 + if (n > 3) { + D = sx4 * (sx2 * n - sx * sx) - sx3 * (sx3 * n - sx * sx2) + sx2 * (sx3 * sx - sx2 * sx2) + Da = sx2y * (sx2 * n - sx * sx) - sxy * (sx3 * n - sx * sx2) + sy * (sx3 * sx - sx2 * sx2) + Db = sx4 * (sxy * n - sy * sx) - sx3 * (sx2y * n - sy * sx2) + sx2 * (sx2y * sx - sxy * sx2) + Dc = sx4 * (sx2 * sy - sx * sxy) - sx3 * (sx3 * sy - sx * sx2y) + sx2 * (sx3 * sxy - sx2 * sx2y) + } + if (D != 0.0) { + val a: Double = Da / D + b = Db / D // b defined in linear fit !? + val c: Double = Dc / D + val yMean = sy / n + var sSquares = 0.0 + var sResidualSquares = 0.0 + for (j in 0..i) { + val before = data[j] + sSquares += Math.pow(before.value / scaleBg - yMean, 2.0) + val deltaT: Double = (before.timestamp - time0) / 1000.0 / scaleTime + val bgj: Double = a * Math.pow(deltaT, 2.0) + b * deltaT + c + sResidualSquares += Math.pow(before.value / scaleBg - bgj, 2.0) + } + var rSqu = 0.64 + if (sSquares != 0.0) { + rSqu = 1 - sResidualSquares / sSquares + } + if (n > 3) { + if (rSqu >= corrMax) { + corrMax = rSqu + // double delta_t = (then_date - time_0) / 1000; + duraP = -ti * scaleTime / 60.0 // remember we are going backwards in time + val delta5Min = 5 * 60 / scaleTime + deltaPl = -scaleBg * (a * Math.pow(-delta5Min, 2.0) - b * delta5Min) // 5 minute slope from last fitted bg starting from last bg, i.e. t=0 + deltaPn = scaleBg * (a * Math.pow( delta5Min, 2.0) + b * delta5Min) // 5 minute slope to next fitted bg starting from last bg, i.e. t=0 + bgAcceleration = 2 * a * scaleBg + a0 = c * scaleBg + a1 = b * scaleBg + a2 = a * scaleBg + bestA = a * scaleBg + bestB = b * scaleBg + bestC = c * scaleBg + } + } + } + } + ppDebug = ppDebug + " coeffs=(" + bestA + " / " + bestB + " / " + bestC + "); bg date=" + time0 + } + // Ende + return GlucoseStatus( glucose = now.value, date = nowDate, @@ -97,7 +321,21 @@ class GlucoseStatusProvider @Inject constructor( shortAvgDelta = shortAverageDelta, delta = delta, longAvgDelta = average(longDeltas), - ).also { aapsLogger.debug(LTag.GLUCOSE, it.log()) }.asRounded() + dura_ISF_average = oldavg, + dura_ISF_minutes = minutesdur.toDouble(), + slope05 = slope05, + slope15 = slope15, + slope40 = slope40, + dura_p = duraP, + delta_pl = deltaPl, + delta_pn = deltaPn, + r_squ = corrMax, + bg_acceleration = bgAcceleration, + a_0 = a0, + a_1 = a1, + a_2 = a2, + pp_debug = ppDebug + ).also { aapsLogger.debug(LTag.GLUCOSE, it.log()) }.asRounded() // +pp_debug } companion object { From ce81e8c0b888e4fd496ee99c844538d11272336d Mon Sep 17 00:00:00 2001 From: Selcuk Kekec Date: Tue, 8 Nov 2022 23:51:56 +0100 Subject: [PATCH 4/9] 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 ff3d0c778a6..b4250d51bb2 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 9c8fe4ba916..e280da95619 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 7d2f7b6f8ce..2d019a410a3 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 6f0b64bc6d2..6be9c34aef0 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 8a954c30abc..33bf396c53f 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 @@ -780,7 +780,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 af527dec052..d0d4d3862f5 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 712ef228956..1dac43f495a 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 0a4072250b5..8a7d7850abc 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 311b1999fa5..dac28baff9d 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 64623dbf122..fe7b5f8e26e 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 30c05b1f322..c66ea28e581 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 12f240b1e32..2535f242ef3 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 347d4bb5732..dc12efa046c 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 3e0a9d2d8cb..adebd91690b 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 a3fd2405700..0c7a489641a 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 7df9fe9729d..4d69884b300 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 05715e8e123..45d9d26b039 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 8efc9651dfb..defe9b72db8 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 6a02daa3221..98e225b1652 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 dd70a31e99e..891617a9379 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 6ada149c6d4..bb0b0dbc1b3 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 072d1953bcf..7a2c6e7a0d2 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 c2115d53e39..5fc097e6cbd 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 da48d8f46a2..61fa45d6976 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 906e84cf367..fead129554c 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 2d161808a6a..6b188c3e3f2 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 c6dee8a6608..ffe959f32fb 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 caf2bc46f4b..f2e08d0425e 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 16ad858693b..d0fa6ec7814 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 db2d6af09d9..14efd0476ff 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 0e94ce025a0..36e5b608dcc 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 22784134590..277dd2e27db 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 9704987c132..4af9e06f932 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1252,5 +1252,10 @@ (No Watch Connected) Error asking for permissions - + + 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 bfeee6b32e4..327fc8902cd 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 55df56b1982..52528d98fdc 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 00a89d48dbf..1fd360a5c11 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 a74fdff40b7..06aac714af5 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 c96b0b405b0..c4216896fd1 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 c62250c04a6..ba1f172079f 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 f722383f5f5..b94f3be0291 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 256d1c8b8b0..68ac4c64362 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 00000000000..24835b62a2b --- /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 948aa705d67..58fc350cec5 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 4f2b1be88d6..cd1f92365dd 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 00979a554cd..e70e4772576 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 382aed5cdeb..9f5d510f8f1 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 ef7fb4f43bbe7d86c825ac80310cbb7264f5ef93 Mon Sep 17 00:00:00 2001 From: asdf Date: Wed, 9 Nov 2022 14:36:17 +0100 Subject: [PATCH 5/9] version change --- app/src/main/java/info/nightscout/androidaps/MainActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.kt b/app/src/main/java/info/nightscout/androidaps/MainActivity.kt index a81af251880..2eaabdebadc 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.kt @@ -343,7 +343,7 @@ class MainActivity : NoSplashAppCompatActivity() { val messageSpanned = SpannableString(message) Linkify.addLinks(messageSpanned, Linkify.WEB_URLS) MaterialAlertDialogBuilder(this, R.style.DialogTheme) - .setTitle(rh.gs(R.string.app_name) + " " + BuildConfig.VERSION + "+autoISF2.2.7") + .setTitle(rh.gs(R.string.app_name) + " " + BuildConfig.VERSION + "+autoISF2.2.7+G7+BackSmoothing") .setIcon(iconsProvider.getIcon()) .setMessage(messageSpanned) .setPositiveButton(rh.gs(R.string.ok), null) From c89b65ce2dfa55c83260f6f007443bc5a7f8c730 Mon Sep 17 00:00:00 2001 From: asdf Date: Wed, 9 Nov 2022 16:28:26 +0100 Subject: [PATCH 6/9] German translation --- app/src/main/res/values-de-rDE/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index 19396b422bd..9689baecadb 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -1046,4 +1046,9 @@ Unerwartetes Verhalten. Blockiert durch Verbindungsoptionen (keine Uhr verbunden) Fehler beim Anfordern der Erlaubnis + + use_data_smoothing + Glättet eingehende Sensordaten im Hauptdiagramm. Empfohlen, wenn die Glukosequelle die Sensormesswerte nicht selbst glättet und SMB immer aktivieren werden sollen. + Glättung eingehender Glucosewerte + Sensordatenglättung ist aktiviert. From 1b897e1b71258cf8d7c353366485ae9348a430cb Mon Sep 17 00:00:00 2001 From: Selcuk Kekec Date: Thu, 10 Nov 2022 23:52:06 +0100 Subject: [PATCH 7/9] 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 6be9c34aef0..3b7d9359970 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 7b34ba26c3a..b10759f9d84 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 d0d4d3862f5..2bdd4621b77 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 d0fa6ec7814..f6d107635a4 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 86cd54dc5288d1c07ff74a65839c345a665e9cd2 Mon Sep 17 00:00:00 2001 From: Selcuk Kekec Date: Sun, 13 Nov 2022 23:30:46 +0100 Subject: [PATCH 8/9] Smoothing: RAW BG switch in graph menu integrated (see commit #8263581) (cherry picked from commit 0174b9535bbd08e34293de73549aa830a41bb40a) --- .../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 | 8 ++++++-- app/src/main/res/values/strings.xml | 2 ++ 6 files changed, 15 insertions(+), 7 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 73547e31c29..a7b0acce2fd 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 33bf396c53f..3fb6410d574 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 @@ -1000,7 +1000,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 2431dc5f617..3a96d8725ab 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 b10759f9d84..26c2e0da379 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 9689baecadb..65c5bfebc5f 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 @@ -1047,8 +1049,10 @@ Unerwartetes Verhalten. (keine Uhr verbunden) Fehler beim Anfordern der Erlaubnis + use_data_smoothing - Glättet eingehende Sensordaten im Hauptdiagramm. Empfohlen, wenn die Glukosequelle die Sensormesswerte nicht selbst glättet und SMB immer aktivieren werden sollen. - Glättung eingehender Glucosewerte + 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 2745549dc94..7b61b22dbf4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -507,6 +507,7 @@ BAS DEV ACT + RAW -BGI ABS DEVSLOPE @@ -723,6 +724,7 @@ Subcategory Bolus will be recorded only (not delivered by pump) SMB set by pump + Raw BG Activity Blood Glucose Impact Sensitivity From ad9bd53c3190d392ee4d61a95c7bdd86fab2eb10 Mon Sep 17 00:00:00 2001 From: Selcuk Kekec Date: Fri, 25 Nov 2022 18:51:31 +0100 Subject: [PATCH 9/9] Merged autoisf 2.2.7 into blaqone flavour branch --- .../plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt | 3 ++- app/src/main/res/values/strings.xml | 5 ++++- app/src/main/res/xml/pref_openapssmb.xml | 8 +++++++- .../plugins/iob/iobCobCalculator/GlucoseStatusProvider.kt | 4 ++-- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt index fad213dbec0..3f5bda89377 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt @@ -245,6 +245,7 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: this.profile.put("smb_delivery_ratio_max", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_smb_delivery_ratio_max, "0.5"))) this.profile.put("smb_delivery_ratio_bg_range", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_smb_delivery_ratio_bg_range, "0"))) this.profile.put("smb_max_range_extension", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_smb_max_range_extension, "1.0"))) + this.profile.put("enableSMB_EvenOn_OddOff", sp.getBoolean(R.string.key_enableSMB_EvenOn_OddOff, false)) if (profileFunction.getUnits() == GlucoseUnit.MMOL) { this.profile.put("out_units", "mmol/L") @@ -300,7 +301,7 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: this.microBolusAllowed = microBolusAllowed smbAlwaysAllowed = advancedFiltering currentTime = now - saveCgmSource = true // for non_Libre use; was: isSaveCgmSource + saveCgmSource = true // for non_Libre use; was: isSaveCgmSource } private fun makeParam(jsonObject: JSONObject?, rhino: Context, scope: Scriptable): Any { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7b61b22dbf4..d1323c6286f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -358,7 +358,7 @@ Default value: 0.7\nThe other side of the autosens safety limits, putting a cap on how low autosens can adjust basals, and how high it can adjust ISF and BG targets. autoISF_max autoISF_min - Default value: 1.2\nThis is a multiplier cap for autoISF to set a limit on how high the autoISF ratio can be, which in turn determines how low it can adjust ISF. + Default value: 1.2\nThis is a multiplier cap for autoISF to set a limit on how high the autoISF ratio can be, which in turn determines how low it can adjust ISF.\n\nWARNING: Be exteremely careful when using vales above 2.5 Default value: 1.0\nThis is a multiplier cap for autoISF to set a limit on how low the autoISF ratio can be, which in turn determines how high it can adjust ISF. dura_ISF_weight Default value: 0.0\nThis is the rate at which autoISF grows per hour assuming bg is twice the target. With a value of 1.0 it will have reduced ISF to 50% after 1 hour of bg at twice the target.\n\nWith 0.0 the effect of autoISF is effectively disabled. @@ -819,6 +819,9 @@ Weight while BG accelerates\n(bgAccel_ISF_weight) Weight while BG decelerates\n(bgBrake_ISF_weight) dura_ISF weight\n(dura_ISF_weight) + Enable alternative activation of SMB depending on TempTarget + Enable alternative activation of SMB + ON if TempTarget is even number\nOFF if TempTarget is odd number Bolus snooze dia divisor Max daily safety multiplier Current basal safety multiplier diff --git a/app/src/main/res/xml/pref_openapssmb.xml b/app/src/main/res/xml/pref_openapssmb.xml index f8785075231..74158d339bf 100644 --- a/app/src/main/res/xml/pref_openapssmb.xml +++ b/app/src/main/res/xml/pref_openapssmb.xml @@ -240,7 +240,7 @@ android:selectAllOnFocus="true" android:singleLine="true" android:title="@string/openapsama_autoISF_max" - validate:floatmaxNumber="3" + validate:floatmaxNumber="5" validate:floatminNumber="1" validate:testType="floatNumericRange" /> @@ -484,6 +484,12 @@ validate:floatminNumber="1" validate:testType="floatNumericRange" /> + + 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 168c54bbee9..332ea062aeb 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 @@ -140,8 +140,8 @@ class GlucoseStatusProvider @Inject constructor( break } } - var autoISFAverage = oldavg - var autoISFDuration = minutesdur.toDouble() + // var autoISFAverage = oldavg no longer used + // var autoISFDuration = minutesdur.toDouble() no longer used // mod 8: calculate 3 variables for deltas based on linear regression // initially just test the handling of arguments