From 063d3685e1d68698593b2e39013c94129906278e Mon Sep 17 00:00:00 2001 From: Brian Quinion Date: Tue, 10 Mar 2020 11:24:40 +0000 Subject: [PATCH] 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" /> + + + +