Show carb suggestions from OpenAPS

This commit is contained in:
Brian Quinion 2020-03-10 11:24:40 +00:00
parent 5cab10f51f
commit 063d3685e1
6 changed files with 127 additions and 35 deletions

View file

@ -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); console.error("naive_eventualBG:",naive_eventualBG,"bgUndershoot:",bgUndershoot,"zeroTempDuration:",zeroTempDuration,"zeroTempEffect:",zeroTempEffect,"carbsReq:",carbsReq);
if ( carbsReq >= profile.carbsReqThreshold && minutesAboveThreshold <= 45 ) { if ( carbsReq >= profile.carbsReqThreshold && minutesAboveThreshold <= 45 ) {
rT.carbsReq = carbsReq; rT.carbsReq = carbsReq;
rT.carbsReqWithin = minutesAboveThreshold;
rT.reason += carbsReq + " add'l carbs req w/in " + minutesAboveThreshold + "m; "; 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 // 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) { 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); rT.reason += "IOB "+iob_data.iob+" < " + round(-profile.current_basal*20/60,2);

View file

@ -62,6 +62,9 @@ public class APSResult {
public double smb = 0d; // super micro bolus in units public double smb = 0d; // super micro bolus in units
public long deliverAt = 0; public long deliverAt = 0;
public int carbsReq = 0;
public int carbsReqWithin = 0;
public Constraint<Double> inputConstraints; public Constraint<Double> inputConstraints;
public Constraint<Double> rateConstraint; public Constraint<Double> rateConstraint;
@ -98,6 +101,10 @@ public class APSResult {
return this; return this;
} }
public String getCarbsRequiredText() {
return String.format(resourceHelper.gs(R.string.carbsreq), carbsReq, carbsReqWithin);
}
@Override @Override
public String toString() { public String toString() {
final PumpInterface pump = activePluginProvider.getActivePump(); final PumpInterface pump = activePluginProvider.getActivePump();
@ -121,11 +128,20 @@ public class APSResult {
if (smb != 0) if (smb != 0)
ret += ("SMB: " + DecimalFormatter.toPumpSupportedBolus(smb) + " U\n"); ret += ("SMB: " + DecimalFormatter.toPumpSupportedBolus(smb) + " U\n");
if (isCarbsRequired()) {
ret += getCarbsRequiredText()+"\n";
}
// reason // reason
ret += resourceHelper.gs(R.string.reason) + ": " + reason; ret += resourceHelper.gs(R.string.reason) + ": " + reason;
return ret; return ret;
} else }
return resourceHelper.gs(R.string.nochangerequested);
if (isCarbsRequired()) {
return getCarbsRequiredText();
}
return resourceHelper.gs(R.string.nochangerequested);
} }
public Spanned toSpanned() { public Spanned toSpanned() {
@ -150,11 +166,20 @@ public class APSResult {
if (smb != 0) if (smb != 0)
ret += ("<b>" + "SMB" + "</b>: " + DecimalFormatter.toPumpSupportedBolus(smb) + " U<br>"); ret += ("<b>" + "SMB" + "</b>: " + DecimalFormatter.toPumpSupportedBolus(smb) + " U<br>");
if (isCarbsRequired()) {
ret += getCarbsRequiredText()+"<br>";
}
// reason // reason
ret += "<b>" + resourceHelper.gs(R.string.reason) + "</b>: " + reason.replace("<", "&lt;").replace(">", "&gt;"); ret += "<b>" + resourceHelper.gs(R.string.reason) + "</b>: " + reason.replace("<", "&lt;").replace(">", "&gt;");
return Html.fromHtml(ret); 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) { public APSResult newAndClone(HasAndroidInjector injector) {
@ -183,6 +208,8 @@ public class APSResult {
newResult.smbConstraint = smbConstraint; newResult.smbConstraint = smbConstraint;
newResult.percent = percent; newResult.percent = percent;
newResult.usePercent = usePercent; newResult.usePercent = usePercent;
newResult.carbsReq = carbsReq;
newResult.carbsReqWithin = carbsReqWithin;
} }
@ -297,6 +324,10 @@ public class APSResult {
return latest; return latest;
} }
public boolean isCarbsRequired() {
return carbsReq > 0;
}
public boolean isChangeRequested() { public boolean isChangeRequested() {
Constraint<Boolean> closedLoopEnabled = constraintChecker.isClosedLoopAllowed(); Constraint<Boolean> closedLoopEnabled = constraintChecker.isClosedLoopAllowed();
// closed loop mode: handle change at driver level // closed loop mode: handle change at driver level

View file

@ -405,6 +405,24 @@ public class LoopPlugin extends PluginBase {
Constraint<Boolean> closedLoopEnabled = constraintChecker.isClosedLoopAllowed(); Constraint<Boolean> closedLoopEnabled = constraintChecker.isClosedLoopAllowed();
if (closedLoopEnabled.value()) { 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() if (resultAfterConstraints.isChangeRequested()
&& !commandQueue.bolusInQueue() && !commandQueue.bolusInQueue()
&& !commandQueue.isRunning(Command.CommandType.BOLUS)) { && !commandQueue.isRunning(Command.CommandType.BOLUS)) {
@ -463,36 +481,9 @@ public class LoopPlugin extends PluginBase {
if (sp.getBoolean("wearcontrol", false)) { if (sp.getBoolean("wearcontrol", false)) {
builder.setLocalOnly(true); builder.setLocalOnly(true);
} }
presentSuggestion(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");
} else if (allowNotification) { } else if (allowNotification) {
// dismiss notifications dismissSuggestion();
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(Constants.notificationID);
actionStringHandler.get().handleInitiate("cancelChangeRequest");
} }
} }
@ -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() { public void acceptChangeRequest() {
Profile profile = profileFunction.getProfile(); Profile profile = profileFunction.getProfile();
final LoopPlugin lp = this; final LoopPlugin lp = this;

View file

@ -4,10 +4,11 @@ import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import dagger.android.HasAndroidInjector; import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.aps.loop.APSResult; import info.nightscout.androidaps.plugins.aps.loop.APSResult;
import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.SP;
public class DetermineBasalResultSMB extends APSResult { 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("eventualBG")) eventualBG = result.getDouble("eventualBG");
if (result.has("snoozeBG")) snoozeBG = result.getDouble("snoozeBG"); if (result.has("snoozeBG")) snoozeBG = result.getDouble("snoozeBG");
//if (result.has("insulinReq")) insulinReq = result.getDouble("insulinReq"); //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")) { if (result.has("rate") && result.has("duration")) {
tempBasalRequested = true; tempBasalRequested = true;

View file

@ -194,6 +194,8 @@
<string name="enableloop">Enable loop</string> <string name="enableloop">Enable loop</string>
<string name="openloop_newsuggestion">New suggestion available</string> <string name="openloop_newsuggestion">New suggestion available</string>
<string name="carbssuggestion">Carbs Suggestion</string>
<string name="unsupportedclientver">Unsupported version of NSClient</string>
<string name="unsupportednsversion">Unsupported version of Nightscout</string> <string name="unsupportednsversion">Unsupported version of Nightscout</string>
<string name="loopdisabled">LOOP DISABLED BY CONSTRAINTS</string> <string name="loopdisabled">LOOP DISABLED BY CONSTRAINTS</string>
<string name="treatments_wizard_basaliob_label">Basal IOB</string> <string name="treatments_wizard_basaliob_label">Basal IOB</string>
@ -279,6 +281,7 @@
<string name="percent">Percent</string> <string name="percent">Percent</string>
<string name="absolute">Absolute</string> <string name="absolute">Absolute</string>
<string name="canceltemp">Cancel temp basal</string> <string name="canceltemp">Cancel temp basal</string>
<string name="carbsreq">"%dg Additional Carbs Required Within %d Minutes</string>
<string name="smscommunicator">SMS Communicator</string> <string name="smscommunicator">SMS Communicator</string>
<string name="waitingforpumpresult">Waiting for result</string> <string name="waitingforpumpresult">Waiting for result</string>
<string name="smscommunicator_allowednumbers">Allowed phone numbers</string> <string name="smscommunicator_allowednumbers">Allowed phone numbers</string>
@ -714,10 +717,16 @@
<string name="smb_shortname">SMB</string> <string name="smb_shortname">SMB</string>
<string name="key_use_smb" translatable="false">use_smb</string> <string name="key_use_smb" translatable="false">use_smb</string>
<string name="key_use_uam" translatable="false">use_uam</string> <string name="key_use_uam" translatable="false">use_uam</string>
<string name="key_smb_enable_carbs_suggestions" translatable="false">key_smb_enable_carbs_suggestions</string>
<string name="key_smb_enable_carbs_suggestions_threshold" translatable="false">key_smb_enable_carbs_suggestions_threshold</string>
<string name="enableuam">Enable UAM</string> <string name="enableuam">Enable UAM</string>
<string name="enablesmb">Enable SMB</string> <string name="enablesmb">Enable SMB</string>
<string name="enablecarbssuggestions">Enable Carbs Suggestions</string>
<string name="enable_carbs_suggestions_threshold">Minimum Carbs Required For Suggestion</string>
<string name="enable_carbs_suggestions_threshold_summary">Minimum grams of carbs to display a carbs suggestion alert. Carbs suggestions below this number will not trigger a notification.</string>
<string name="enablesmb_summary">Use Super Micro Boluses instead of temp basal for faster action</string> <string name="enablesmb_summary">Use Super Micro Boluses instead of temp basal for faster action</string>
<string name="enableuam_summary">Detection of Unannounced meals</string> <string name="enableuam_summary">Detection of Unannounced meals</string>
<string name="enablecarbssuggestions_summary">Enable carbs suggestions when BG is predicted to go below threshold</string>
<string name="key_insulin_oref_peak" translatable="false">insulin_oref_peak</string> <string name="key_insulin_oref_peak" translatable="false">insulin_oref_peak</string>
<string name="insulin_oref_peak">IOB Curve Peak Time</string> <string name="insulin_oref_peak">IOB Curve Peak Time</string>
<string name="insulin_peak_time">Peak Time [min]</string> <string name="insulin_peak_time">Peak Time [min]</string>

View file

@ -103,6 +103,25 @@
android:summary="@string/low_temptarget_lowers_sensitivity_summary" android:summary="@string/low_temptarget_lowers_sensitivity_summary"
android:title="@string/low_temptarget_lowers_sensitivity_title" /> android:title="@string/low_temptarget_lowers_sensitivity_title" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_smb_enable_carbs_suggestions"
android:summary="@string/enablecarbssuggestions_summary"
android:title="@string/enablecarbssuggestions" />
<info.nightscout.androidaps.utils.textValidator.ValidatingEditTextPreference
android:defaultValue="0"
android:dialogMessage="@string/enable_carbs_suggestions_threshold_summary"
android:digits="0123456789"
android:inputType="number"
android:key="@string/key_smb_enable_carbs_suggestions_threshold"
android:maxLines="20"
android:selectAllOnFocus="true"
android:title="@string/enable_carbs_suggestions_threshold"
validate:maxNumber="20"
validate:minNumber="0"
validate:testType="numericRange" />
<androidx.preference.PreferenceScreen <androidx.preference.PreferenceScreen
android:key="absorption_smb_advanced" android:key="absorption_smb_advanced"
android:title="@string/advancedsettings_title"> android:title="@string/advancedsettings_title">