From 063d3685e1d68698593b2e39013c94129906278e Mon Sep 17 00:00:00 2001 From: Brian Quinion Date: Tue, 10 Mar 2020 11:24:40 +0000 Subject: [PATCH 1/3] Show carb suggestions from OpenAPS --- .../main/assets/OpenAPSSMB/determine-basal.js | 2 + .../plugins/aps/loop/APSResult.java | 39 ++++++++- .../plugins/aps/loop/LoopPlugin.java | 83 ++++++++++++------- .../openAPSSMB/DetermineBasalResultSMB.java | 10 ++- app/src/main/res/values/strings.xml | 9 ++ app/src/main/res/xml/pref_openapssmb.xml | 19 +++++ 6 files changed, 127 insertions(+), 35 deletions(-) diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js index c2db0f270b..11a66df891 100644 --- a/app/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -862,8 +862,10 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ console.error("naive_eventualBG:",naive_eventualBG,"bgUndershoot:",bgUndershoot,"zeroTempDuration:",zeroTempDuration,"zeroTempEffect:",zeroTempEffect,"carbsReq:",carbsReq); if ( carbsReq >= profile.carbsReqThreshold && minutesAboveThreshold <= 45 ) { rT.carbsReq = carbsReq; + rT.carbsReqWithin = minutesAboveThreshold; rT.reason += carbsReq + " add'l carbs req w/in " + minutesAboveThreshold + "m; "; } + // don't low glucose suspend if IOB is already super negative and BG is rising faster than predicted if (bg < threshold && iob_data.iob < -profile.current_basal*20/60 && minDelta > 0 && minDelta > expectedDelta) { rT.reason += "IOB "+iob_data.iob+" < " + round(-profile.current_basal*20/60,2); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.java index 0c91d0723c..27ebb5dd84 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/APSResult.java @@ -62,6 +62,9 @@ public class APSResult { public double smb = 0d; // super micro bolus in units public long deliverAt = 0; + public int carbsReq = 0; + public int carbsReqWithin = 0; + public Constraint inputConstraints; public Constraint rateConstraint; @@ -98,6 +101,10 @@ public class APSResult { return this; } + public String getCarbsRequiredText() { + return String.format(resourceHelper.gs(R.string.carbsreq), carbsReq, carbsReqWithin); + } + @Override public String toString() { final PumpInterface pump = activePluginProvider.getActivePump(); @@ -121,11 +128,20 @@ public class APSResult { if (smb != 0) ret += ("SMB: " + DecimalFormatter.toPumpSupportedBolus(smb) + " U\n"); + if (isCarbsRequired()) { + ret += getCarbsRequiredText()+"\n"; + } + // reason ret += resourceHelper.gs(R.string.reason) + ": " + reason; return ret; - } else - return resourceHelper.gs(R.string.nochangerequested); + } + + if (isCarbsRequired()) { + return getCarbsRequiredText(); + } + + return resourceHelper.gs(R.string.nochangerequested); } public Spanned toSpanned() { @@ -150,11 +166,20 @@ public class APSResult { if (smb != 0) ret += ("" + "SMB" + ": " + DecimalFormatter.toPumpSupportedBolus(smb) + " U
"); + if (isCarbsRequired()) { + ret += getCarbsRequiredText()+"
"; + } + // reason ret += "" + resourceHelper.gs(R.string.reason) + ": " + reason.replace("<", "<").replace(">", ">"); return Html.fromHtml(ret); - } else - return Html.fromHtml(resourceHelper.gs(R.string.nochangerequested)); + } + + if (isCarbsRequired()) { + return Html.fromHtml(getCarbsRequiredText()); + } + + return Html.fromHtml(resourceHelper.gs(R.string.nochangerequested)); } public APSResult newAndClone(HasAndroidInjector injector) { @@ -183,6 +208,8 @@ public class APSResult { newResult.smbConstraint = smbConstraint; newResult.percent = percent; newResult.usePercent = usePercent; + newResult.carbsReq = carbsReq; + newResult.carbsReqWithin = carbsReqWithin; } @@ -297,6 +324,10 @@ public class APSResult { return latest; } + public boolean isCarbsRequired() { + return carbsReq > 0; + } + public boolean isChangeRequested() { Constraint closedLoopEnabled = constraintChecker.isClosedLoopAllowed(); // closed loop mode: handle change at driver level diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java index 40ba607cd1..c386ce6f0a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java @@ -405,6 +405,24 @@ public class LoopPlugin extends PluginBase { Constraint closedLoopEnabled = constraintChecker.isClosedLoopAllowed(); if (closedLoopEnabled.value()) { + + if (allowNotification) { + if (resultAfterConstraints.isCarbsRequired()) { + NotificationCompat.Builder builder = + new NotificationCompat.Builder(MainApp.instance().getApplicationContext(), CHANNEL_ID); + builder.setSmallIcon(R.drawable.notif_icon) + .setContentTitle(MainApp.gs(R.string.carbssuggestion)) + .setContentText(resultAfterConstraints.getCarbsRequiredText()) + .setAutoCancel(true) + .setPriority(Notification.PRIORITY_MAX) + .setCategory(Notification.CATEGORY_ALARM) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC); + presentSuggestion(builder); + } else { + dismissSuggestion(); + } + } + if (resultAfterConstraints.isChangeRequested() && !commandQueue.bolusInQueue() && !commandQueue.isRunning(Command.CommandType.BOLUS)) { @@ -463,36 +481,9 @@ public class LoopPlugin extends PluginBase { if (sp.getBoolean("wearcontrol", false)) { builder.setLocalOnly(true); } - - // Creates an explicit intent for an Activity in your app - Intent resultIntent = new Intent(context, MainActivity.class); - - // The stack builder object will contain an artificial back stack for the - // started Activity. - // This ensures that navigating backward from the Activity leads out of - // your application to the Home screen. - TaskStackBuilder stackBuilder = TaskStackBuilder.create(context); - stackBuilder.addParentStack(MainActivity.class); - // Adds the Intent that starts the Activity to the top of the stack - stackBuilder.addNextIntent(resultIntent); - PendingIntent resultPendingIntent = - stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); - builder.setContentIntent(resultPendingIntent); - builder.setVibrate(new long[]{1000, 1000, 1000, 1000, 1000}); - NotificationManager mNotificationManager = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - // mId allows you to update the notification later on. - mNotificationManager.notify(Constants.notificationID, builder.build()); - rxBus.send(new EventNewOpenLoopNotification()); - - // Send to Wear - actionStringHandler.get().handleInitiate("changeRequest"); + presentSuggestion(builder); } else if (allowNotification) { - // dismiss notifications - NotificationManager notificationManager = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.cancel(Constants.notificationID); - actionStringHandler.get().handleInitiate("cancelChangeRequest"); + dismissSuggestion(); } } @@ -502,6 +493,40 @@ public class LoopPlugin extends PluginBase { } } + private void presentSuggestion(NotificationCompat.Builder builder) { + // Creates an explicit intent for an Activity in your app + Intent resultIntent = new Intent(context, MainActivity.class); + + // The stack builder object will contain an artificial back stack for the + // started Activity. + // This ensures that navigating backward from the Activity leads out of + // your application to the Home screen. + TaskStackBuilder stackBuilder = TaskStackBuilder.create(context); + stackBuilder.addParentStack(MainActivity.class); + // Adds the Intent that starts the Activity to the top of the stack + stackBuilder.addNextIntent(resultIntent); + PendingIntent resultPendingIntent = + stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); + builder.setContentIntent(resultPendingIntent); + builder.setVibrate(new long[]{1000, 1000, 1000, 1000, 1000}); + NotificationManager mNotificationManager = + (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + // mId allows you to update the notification later on. + mNotificationManager.notify(Constants.notificationID, builder.build()); + rxBus.send(new EventNewOpenLoopNotification()); + + // Send to Wear + actionStringHandler.get().handleInitiate("changeRequest"); + } + + private void dismissSuggestion() { + // dismiss notifications + NotificationManager notificationManager = + (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancel(Constants.notificationID); + actionStringHandler.get().handleInitiate("cancelChangeRequest"); + } + public void acceptChangeRequest() { Profile profile = profileFunction.getProfile(); final LoopPlugin lp = this; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.java index dae170d5d9..746737ec0f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.java @@ -4,10 +4,11 @@ import org.json.JSONException; import org.json.JSONObject; import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; +import info.nightscout.androidaps.R; import info.nightscout.androidaps.plugins.aps.loop.APSResult; import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.utils.SP; public class DetermineBasalResultSMB extends APSResult { @@ -33,7 +34,12 @@ public class DetermineBasalResultSMB extends APSResult { if (result.has("eventualBG")) eventualBG = result.getDouble("eventualBG"); if (result.has("snoozeBG")) snoozeBG = result.getDouble("snoozeBG"); //if (result.has("insulinReq")) insulinReq = result.getDouble("insulinReq"); - //if (result.has("carbsReq")) carbsReq = result.getDouble("carbsReq"); + + if (SP.getBoolean(R.string.key_smb_enable_carbs_suggestions, false)) { + if (result.has("carbsReq") && result.getInt("carbsReq") >= SP.getInt(R.string.key_smb_enable_carbs_suggestions_threshold, 0)) + carbsReq = result.getInt("carbsReq"); + if (result.has("carbsReqWithin")) carbsReqWithin = result.getInt("carbsReqWithin"); + } if (result.has("rate") && result.has("duration")) { tempBasalRequested = true; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1f15a23d70..d9d0e04ab3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -194,6 +194,8 @@ Enable loop New suggestion available + Carbs Suggestion + Unsupported version of NSClient Unsupported version of Nightscout LOOP DISABLED BY CONSTRAINTS Basal IOB @@ -279,6 +281,7 @@ Percent Absolute Cancel temp basal + "%dg Additional Carbs Required Within %d Minutes SMS Communicator Waiting for result Allowed phone numbers @@ -714,10 +717,16 @@ SMB use_smb use_uam + key_smb_enable_carbs_suggestions + key_smb_enable_carbs_suggestions_threshold Enable UAM Enable SMB + Enable Carbs Suggestions + Minimum Carbs Required For Suggestion + Minimum grams of carbs to display a carbs suggestion alert. Carbs suggestions below this number will not trigger a notification. Use Super Micro Boluses instead of temp basal for faster action Detection of Unannounced meals + Enable carbs suggestions when BG is predicted to go below threshold insulin_oref_peak IOB Curve Peak Time Peak Time [min] diff --git a/app/src/main/res/xml/pref_openapssmb.xml b/app/src/main/res/xml/pref_openapssmb.xml index 0cecf93c73..b020a0a9b1 100644 --- a/app/src/main/res/xml/pref_openapssmb.xml +++ b/app/src/main/res/xml/pref_openapssmb.xml @@ -103,6 +103,25 @@ android:summary="@string/low_temptarget_lowers_sensitivity_summary" android:title="@string/low_temptarget_lowers_sensitivity_title" /> + + + + From b1097880f4db42e93c47a3fe67a73b94f21bccf0 Mon Sep 17 00:00:00 2001 From: Brian Quinion Date: Tue, 10 Mar 2020 18:46:14 +0000 Subject: [PATCH 2/3] Add carbs required to overview. Add notification with option to ignore carbs suggestions for 5, 15 or 30min --- app/src/main/AndroidManifest.xml | 3 ++ .../dependencyInjection/ReceiversModule.kt | 3 ++ .../aps/loop/CarbSuggestionReceiver.java | 20 ++++++++ .../plugins/aps/loop/LoopPlugin.java | 51 ++++++++++++++++--- .../general/overview/OverviewFragment.java | 14 +++++ app/src/main/res/layout/overview_fragment.xml | 12 +++++ app/src/main/res/values/strings.xml | 2 + 7 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/CarbSuggestionReceiver.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7dae93317c..8e8c96b965 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -113,6 +113,9 @@ + + + 0) { + cobRequiredView.setVisibility(View.VISIBLE); + String carbsRequiredString = String.format(resourceHelper.gs(R.string.overview_carbs_required), loopPlugin.lastRun.constraintsProcessed.carbsReq, loopPlugin.lastRun.constraintsProcessed.carbsReqWithin); + cobRequiredView.setText(carbsRequiredString); + } else { + cobRequiredView.setVisibility(View.GONE); + } + } + if (statuslightsLayout != null) if (sp.getBoolean(R.string.key_show_statuslights, false)) { if (sp.getBoolean(R.string.key_show_statuslights_extended, false)) { diff --git a/app/src/main/res/layout/overview_fragment.xml b/app/src/main/res/layout/overview_fragment.xml index 467c8c171a..70e1255275 100644 --- a/app/src/main/res/layout/overview_fragment.xml +++ b/app/src/main/res/layout/overview_fragment.xml @@ -266,6 +266,18 @@ app:layout_constraintBottom_toBottomOf="@+id/overview_cob_label" app:layout_constraintStart_toEndOf="@+id/overview_cob_colon" /> + + SMB execution time Temp basal request time Temp basal execution time + (20g needed in 45min) + (%dg needed in %dmin) From 72a3e825e3a0f52f252f5aca41f2208ab44e0258 Mon Sep 17 00:00:00 2001 From: Brian Quinion Date: Tue, 10 Mar 2020 18:59:39 +0000 Subject: [PATCH 3/3] Move carbs_suggestions_threshold to LoopPlugin so that non-critical carbs still show on overview (but no system notification) --- .../nightscout/androidaps/plugins/aps/loop/LoopPlugin.java | 4 +++- .../plugins/aps/openAPSSMB/DetermineBasalResultSMB.java | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java index eecb0d3e25..d245bdd816 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java @@ -409,7 +409,9 @@ public class LoopPlugin extends PluginBase { if (closedLoopEnabled.value()) { if (allowNotification) { - if (resultAfterConstraints.isCarbsRequired() && carbsSuggestionsSuspendedUntil < System.currentTimeMillis()) { + if (resultAfterConstraints.isCarbsRequired() + && resultAfterConstraints.carbsReq >= sp.getInt(R.string.key_smb_enable_carbs_suggestions_threshold, 0) + && carbsSuggestionsSuspendedUntil < System.currentTimeMillis()) { Intent intentAction5m = new Intent(context, CarbSuggestionReceiver.class); intentAction5m.putExtra("ignoreDuration",5); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.java index 746737ec0f..0c0c5e17ed 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.java @@ -36,8 +36,7 @@ public class DetermineBasalResultSMB extends APSResult { //if (result.has("insulinReq")) insulinReq = result.getDouble("insulinReq"); if (SP.getBoolean(R.string.key_smb_enable_carbs_suggestions, false)) { - if (result.has("carbsReq") && result.getInt("carbsReq") >= SP.getInt(R.string.key_smb_enable_carbs_suggestions_threshold, 0)) - carbsReq = result.getInt("carbsReq"); + if (result.has("carbsReq")) carbsReq = result.getInt("carbsReq"); if (result.has("carbsReqWithin")) carbsReqWithin = result.getInt("carbsReqWithin"); }