initial work on SMB

This commit is contained in:
Milos Kozak 2017-08-15 23:13:37 +02:00
parent 82d3289602
commit 898e162300
15 changed files with 1073 additions and 79 deletions

View file

@ -44,7 +44,7 @@ android {
minSdkVersion 21
targetSdkVersion 23
versionCode 1500
version "1.5g"
version "1.5smb"
buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", generateGitBuild()
}

View file

@ -194,17 +194,17 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
// if eventualBG, naive_eventualBG, and target_bg aren't all above adjustedMinBG, dont use it
//console.error("naive_eventualBG:",naive_eventualBG+", eventualBG:",eventualBG);
if (eventualBG > adjustedMinBG && naive_eventualBG > adjustedMinBG && min_bg > adjustedMinBG) {
process.stderr.write("Adjusting targets for high BG: min_bg from "+min_bg+" to "+adjustedMinBG+"; ");
console.error("Adjusting targets for high BG: min_bg from "+min_bg+" to "+adjustedMinBG+"; ");
min_bg = adjustedMinBG;
} else {
process.stderr.write("min_bg unchanged: "+min_bg+"; ");
console.error("min_bg unchanged: "+min_bg+"; ");
}
// if eventualBG, naive_eventualBG, and target_bg aren't all above adjustedTargetBG, dont use it
if (eventualBG > adjustedTargetBG && naive_eventualBG > adjustedTargetBG && target_bg > adjustedTargetBG) {
process.stderr.write("target_bg from "+target_bg+" to "+adjustedTargetBG+"; ");
console.error("target_bg from "+target_bg+" to "+adjustedTargetBG+"; ");
target_bg = adjustedTargetBG;
} else {
process.stderr.write("target_bg unchanged: "+target_bg+"; ");
console.error("target_bg unchanged: "+target_bg+"; ");
}
// if eventualBG, naive_eventualBG, and max_bg aren't all above adjustedMaxBG, dont use it
if (eventualBG > adjustedMaxBG && naive_eventualBG > adjustedMaxBG && max_bg > adjustedMaxBG) {

View file

@ -38,6 +38,7 @@ import info.nightscout.androidaps.plugins.NSClientInternal.NSClientInternalFragm
import info.nightscout.androidaps.plugins.NSClientInternal.receivers.AckAlarmReceiver;
import info.nightscout.androidaps.plugins.OpenAPSAMA.OpenAPSAMAFragment;
import info.nightscout.androidaps.plugins.OpenAPSMA.OpenAPSMAFragment;
import info.nightscout.androidaps.plugins.OpenAPSSMB.OpenAPSSMBFragment;
import info.nightscout.androidaps.plugins.Overview.OverviewFragment;
import info.nightscout.androidaps.plugins.Persistentnotification.PersistentNotificationPlugin;
import info.nightscout.androidaps.plugins.ProfileCircadianPercentage.CircadianPercentageProfileFragment;
@ -125,6 +126,7 @@ public class MainApp extends Application {
if (Config.LOOPENABLED) pluginsList.add(LoopFragment.getPlugin());
if (Config.OPENAPSENABLED) pluginsList.add(OpenAPSMAFragment.getPlugin());
if (Config.OPENAPSENABLED) pluginsList.add(OpenAPSAMAFragment.getPlugin());
if (Config.OPENAPSENABLED) pluginsList.add(OpenAPSSMBFragment.getPlugin());
pluginsList.add(NSProfileFragment.getPlugin());
if (Config.OTHERPROFILES) pluginsList.add(SimpleProfileFragment.getPlugin());
if (Config.OTHERPROFILES) pluginsList.add(LocalProfileFragment.getPlugin());

View file

@ -14,6 +14,7 @@ import android.preference.PreferenceManager;
import info.nightscout.androidaps.events.EventPreferenceChange;
import info.nightscout.androidaps.events.EventRefreshGui;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.plugins.OpenAPSSMB.OpenAPSSMBPlugin;
import info.nightscout.androidaps.plugins.PumpDanaR.BluetoothDevicePreference;
import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPlugin;
import info.nightscout.androidaps.plugins.PumpDanaRKorean.DanaRKoreanPlugin;
@ -112,6 +113,8 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
addPreferencesFromResource(R.xml.pref_openapsma);
if (MainApp.getSpecificPlugin(OpenAPSAMAPlugin.class) != null && MainApp.getSpecificPlugin(OpenAPSAMAPlugin.class).isEnabled(PluginBase.APS))
addPreferencesFromResource(R.xml.pref_openapsama);
if (MainApp.getSpecificPlugin(OpenAPSSMBPlugin.class) != null && MainApp.getSpecificPlugin(OpenAPSSMBPlugin.class).isEnabled(PluginBase.APS))
addPreferencesFromResource(R.xml.pref_openapsama);
}
if (MainApp.getSpecificPlugin(SensitivityAAPSPlugin.class) != null && MainApp.getSpecificPlugin(SensitivityAAPSPlugin.class).isEnabled(PluginBase.SENSITIVITY)
|| MainApp.getSpecificPlugin(SensitivityWeightedAveragePlugin.class) != null && MainApp.getSpecificPlugin(SensitivityWeightedAveragePlugin.class).isEnabled(PluginBase.SENSITIVITY))

View file

@ -19,6 +19,7 @@ public class IobTotal {
// oref1
public double microBolusInsulin;
public double microBolusIOB;
public long lastBolusTime;
public double netInsulin = 0d; // for calculations from temp basals only
public double netRatio = 0d; // net ratio at start of temp basal
@ -36,6 +37,7 @@ public class IobTotal {
this.hightempinsulin = 0d;
this.microBolusInsulin = 0d;
this.microBolusIOB = 0d;
this.lastBolusTime = 0;
this.time = time;
}
@ -63,6 +65,7 @@ public class IobTotal {
result.hightempinsulin = basalIob.hightempinsulin;
result.microBolusInsulin = bolusIOB.microBolusInsulin + basalIob.microBolusInsulin;
result.microBolusIOB = bolusIOB.microBolusIOB + basalIob.microBolusIOB;
result.lastBolusTime = bolusIOB.lastBolusTime;
return result;
}
@ -98,6 +101,7 @@ public class IobTotal {
json.put("basaliob", basaliob);
json.put("bolussnooze", bolussnooze);
json.put("activity", activity);
json.put("lastBolusTime", lastBolusTime);
json.put("time", DateUtil.toISOString(new Date(time)));
} catch (JSONException e) {
e.printStackTrace();

View file

@ -7,4 +7,5 @@ public class MealData {
public double boluses = 0d;
public double carbs = 0d;
public double mealCOB = 0.0d;
public double minDeviationSlope;
}

View file

@ -59,6 +59,8 @@ public class AutosensData {
public double autosensRatio = 1d;
public double minDeviationSlope;
public String log(long time) {
return "AutosensData: " + new Date(time).toLocaleString() + " " + pastSensitivity + " Delta=" + delta + " Bgi=" + bgi + " Deviation=" + deviation + " Absorbed=" + absorbed + " CarbsFromBolus=" + carbsFromBolus + " COB=" + cob + " autosensRatio=" + autosensRatio;
}

View file

@ -227,7 +227,7 @@ public class IobCobCalculatorPlugin implements PluginBase {
private BgReading findOlder(long time) {
BgReading lastFound = bgReadings.get(bgReadings.size() - 1);
if (lastFound.date > time) return null;
for (int i = bgReadings.size() - 2; i >=0 ; --i) {
for (int i = bgReadings.size() - 2; i >= 0; --i) {
if (bgReadings.get(i).date < time) continue;
lastFound = bgReadings.get(i);
if (bgReadings.get(i).date > time) break;
@ -246,7 +246,7 @@ public class IobCobCalculatorPlugin implements PluginBase {
long currentTime = bgReadings.get(0).date + 5 * 60 * 1000 - bgReadings.get(0).date % (5 * 60 * 1000) - 5 * 60 * 1000L;
//log.debug("First reading: " + new Date(currentTime).toLocaleString());
while (true) {
while (true) {
// test if current value is older than current time
BgReading newer = findNewer(currentTime);
BgReading older = findOlder(currentTime);
@ -353,6 +353,11 @@ public class IobCobCalculatorPlugin implements PluginBase {
long prevDataTime = roundUpTime(bucketed_data.get(bucketed_data.size() - 3).date);
log.debug("Prev data time: " + new Date(prevDataTime).toLocaleString());
AutosensData previous = autosensDataTable.get(prevDataTime);
double currentDeviation = 0;
double minDeviationSlope = 0;
double maxDeviation = 0;
// start from oldest to be able sub cob
for (int i = bucketed_data.size() - 4; i >= 0; i--) {
// check if data already exists
@ -379,11 +384,13 @@ public class IobCobCalculatorPlugin implements PluginBase {
double bg;
double avgDelta;
double delta;
bg = bucketed_data.get(i).value;
if (bg < 39 || bucketed_data.get(i + 3).value < 39) {
log.error("! value < 39");
continue;
}
avgDelta = (bg - bucketed_data.get(i + 3).value) / 3;
delta = (bg - bucketed_data.get(i + 1).value);
IobTotal iob = calculateFromTreatmentsAndTemps(bgTime);
@ -391,6 +398,32 @@ public class IobCobCalculatorPlugin implements PluginBase {
double bgi = -iob.activity * sens * 5;
double deviation = delta - bgi;
/*
TODO: SMB: i don't undestand this part
long ciTime = System.currentTimeMillis();
if (i == bucketed_data.size() - 4) {
currentDeviation = Math.round((avgDelta - bgi) * 1000) / 1000;
} else if (ciTime > bgTime) {
double avgDeviation = Math.round((avgDelta - bgi) * 1000) / 1000;
double deviationSlope = (avgDeviation - currentDeviation) / (bgTime - ciTime) * 1000 * 60 * 5;
if (avgDeviation > maxDeviation) {
minDeviationSlope = Math.min(0, deviationSlope);
maxDeviation = avgDeviation;
}
//console.error("Deviations:",bgTime, avgDeviation, deviationSlope, minDeviationSlope);
}
https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169
Scott's comment
that stuff is used in UAM (unannounced meal) mode. what we're doing there is calculating the currentDeviation (how fast carbs are absorbing right now), and comparing that to the highest deviation we've seen in the last 45m to calculate the deviationSlope: the rate at which deviations are currently decreasing. We then project deviations to continue into the future starting at the currentDeviation rate and declining at deviationSlope until it gets back down to zero
that gives us an independent way to estimate how long deviations (likely carb absorption) will continue, even if we don't have (or don't want to fully trust) the user's carb estimate
the complexity comes from reusing the same COB calculation code to calculate both current and historical carb absorption rates
*/
List<Treatment> recentTreatments = MainApp.getConfigBuilder().getTreatments5MinBackFromHistory(bgTime);
for (int ir = 0; ir < recentTreatments.size(); ir++) {
autosensData.carbsFromBolus += recentTreatments.get(ir).carbs;
@ -420,6 +453,7 @@ public class IobCobCalculatorPlugin implements PluginBase {
autosensData.deviation = deviation;
autosensData.bgi = bgi;
autosensData.delta = delta;
autosensData.minDeviationSlope = minDeviationSlope;
// calculate autosens only without COB
if (autosensData.cob <= 0) {
@ -669,8 +703,8 @@ public class IobCobCalculatorPlugin implements PluginBase {
for (int index = iobTable.size() - 1; index >= 0; index--) {
if (iobTable.keyAt(index) > time) {
if (Config.logAutosensData)
if (Config.logAutosensData)
log.debug("Removing from iobTable: " + new Date(iobTable.keyAt(index)).toLocaleString());
if (Config.logAutosensData)
log.debug("Removing from iobTable: " + new Date(iobTable.keyAt(index)).toLocaleString());
iobTable.removeAt(index);
} else {
break;

View file

@ -0,0 +1,323 @@
package info.nightscout.androidaps.plugins.OpenAPSSMB;
import com.eclipsesource.v8.JavaVoidCallback;
import com.eclipsesource.v8.V8;
import com.eclipsesource.v8.V8Array;
import com.eclipsesource.v8.V8Object;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Date;
import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.GlucoseStatus;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.MealData;
import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.plugins.Loop.ScriptReader;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.utils.SP;
public class DetermineBasalAdapterSMBJS {
private static Logger log = LoggerFactory.getLogger(DetermineBasalAdapterSMBJS.class);
private ScriptReader mScriptReader = null;
V8 mV8rt;
private V8Object mProfile;
private V8Object mGlucoseStatus;
private V8Array mIobData;
private V8Object mMealData;
private V8Object mCurrentTemp;
private V8Object mAutosensData = null;
private final String PARAM_currentTemp = "currentTemp";
private final String PARAM_iobData = "iobData";
private final String PARAM_glucoseStatus = "glucose_status";
private final String PARAM_profile = "profile";
private final String PARAM_meal_data = "meal_data";
private final String PARAM_autosens_data = "autosens_data";
private final String PARAM_reservoirData = "reservoirData";
private final String PARAM_microBolusAllowed = "microBolusAllowed";
private String storedCurrentTemp = null;
private String storedIobData = null;
private String storedGlucoseStatus = null;
private String storedProfile = null;
private String storedMeal_data = null;
private String storedAutosens_data = null;
private String storedMicroBolusAllowed = null;
private String scriptDebug = "";
/**
* Main code
*/
public DetermineBasalAdapterSMBJS(ScriptReader scriptReader) throws IOException {
mV8rt = V8.createV8Runtime();
mScriptReader = scriptReader;
initLogCallback();
initProcessExitCallback();
initModuleParent();
loadScript();
}
public DetermineBasalResultSMB invoke() {
log.debug(">>> Invoking detemine_basal_oref1 <<<");
log.debug("Glucose status: " + (storedGlucoseStatus = mV8rt.executeStringScript("JSON.stringify(" + PARAM_glucoseStatus + ");")));
log.debug("IOB data: " + (storedIobData = mV8rt.executeStringScript("JSON.stringify(" + PARAM_iobData + ");")));
log.debug("Current temp: " + (storedCurrentTemp = mV8rt.executeStringScript("JSON.stringify(" + PARAM_currentTemp + ");")));
log.debug("Profile: " + (storedProfile = mV8rt.executeStringScript("JSON.stringify(" + PARAM_profile + ");")));
log.debug("Meal data: " + (storedMeal_data = mV8rt.executeStringScript("JSON.stringify(" + PARAM_meal_data + ");")));
if (mAutosensData != null)
log.debug("Autosens data: " + (storedAutosens_data = mV8rt.executeStringScript("JSON.stringify(" + PARAM_autosens_data + ");")));
else
log.debug("Autosens data: " + (storedAutosens_data = "undefined"));
log.debug("Reservoir data: " + "undefined");
log.debug("MicroBolusAllowed: " + (storedMicroBolusAllowed = mV8rt.executeStringScript("JSON.stringify(" + PARAM_microBolusAllowed + ");")));
mV8rt.executeVoidScript(
"var rT = determine_basal(" +
PARAM_glucoseStatus + ", " +
PARAM_currentTemp + ", " +
PARAM_iobData + ", " +
PARAM_profile + ", " +
PARAM_autosens_data + ", " +
PARAM_meal_data + ", " +
"tempBasalFunctions" + ", " +
PARAM_microBolusAllowed + ", " +
PARAM_reservoirData +
");");
String ret = mV8rt.executeStringScript("JSON.stringify(rT);");
log.debug("Result: " + ret);
DetermineBasalResultSMB result = null;
try {
result = new DetermineBasalResultSMB(new JSONObject(ret));
} catch (JSONException e) {
e.printStackTrace();
}
return result;
}
String getGlucoseStatusParam() {
return storedGlucoseStatus;
}
String getCurrentTempParam() {
return storedCurrentTemp;
}
String getIobDataParam() {
return storedIobData;
}
String getProfileParam() {
return storedProfile;
}
String getMealDataParam() {
return storedMeal_data;
}
String getAutosensDataParam() {
return storedAutosens_data;
}
String getMicroBolusAllowedParam() {
return storedMicroBolusAllowed;
}
String getScriptDebug() {
return scriptDebug;
}
private void loadScript() throws IOException {
mV8rt.executeVoidScript("var round_basal = function round_basal(basal, profile) { return basal; };");
mV8rt.executeVoidScript("require = function() {return round_basal;};");
mV8rt.executeVoidScript(readFile("OpenAPSSMB/basal-set-temp.js"), "OpenAPSSMB/basal-set-temp.js ", 0);
mV8rt.executeVoidScript("var tempBasalFunctions = module.exports;");
mV8rt.executeVoidScript(
readFile("OpenAPSSMB/determine-basal.js"),
"OpenAPSSMB/determine-basal.js",
0);
mV8rt.executeVoidScript("var determine_basal = module.exports;");
}
private void initModuleParent() {
mV8rt.executeVoidScript("var module = {\"parent\":Boolean(1)};");
}
private void initProcessExitCallback() {
JavaVoidCallback callbackProccessExit = new JavaVoidCallback() {
@Override
public void invoke(V8Object arg0, V8Array parameters) {
if (parameters.length() > 0) {
Object arg1 = parameters.get(0);
log.error("ProccessExit " + arg1);
}
}
};
mV8rt.registerJavaMethod(callbackProccessExit, "proccessExit");
mV8rt.executeVoidScript("var process = {\"exit\": function () { proccessExit(); } };");
}
private void initLogCallback() {
JavaVoidCallback callbackLog = new JavaVoidCallback() {
@Override
public void invoke(V8Object arg0, V8Array parameters) {
int i = 0;
String s = "";
while (i < parameters.length()) {
Object arg = parameters.get(i);
s += arg + " ";
i++;
}
if (!s.equals("") && Config.logAPSResult) {
log.debug("Script debug: " + s);
scriptDebug += s + "\n";
}
}
};
mV8rt.registerJavaMethod(callbackLog, "log");
mV8rt.executeVoidScript("var console = {\"log\":log, \"error\":log};");
}
public void setData(Profile profile,
double maxIob,
double maxBasal,
double minBg,
double maxBg,
double targetBg,
PumpInterface pump,
IobTotal[] iobArray,
GlucoseStatus glucoseStatus,
MealData mealData,
double autosensDataRatio,
boolean tempTargetSet,
boolean microBolusAllowed
) {
String units = profile.getUnits();
mProfile = new V8Object(mV8rt);
mProfile.add("max_iob", maxIob);
mProfile.add("dia", profile.getDia());
mProfile.add("type", "current");
mProfile.add("max_daily_basal", profile.getMaxDailyBasal());
mProfile.add("max_basal", maxBasal);
mProfile.add("min_bg", minBg);
mProfile.add("max_bg", maxBg);
mProfile.add("target_bg", targetBg);
mProfile.add("carb_ratio", profile.getIc());
mProfile.add("sens", Profile.toMgdl(profile.getIsf().doubleValue(), units));
mProfile.add("max_daily_safety_multiplier", SP.getInt("openapsama_max_daily_safety_multiplier", 3));
mProfile.add("current_basal_safety_multiplier", SP.getInt("openapsama_current_basal_safety_multiplier", 4));
mProfile.add("skip_neutral_temps", true);
mProfile.add("current_basal", pump.getBaseBasalRate());
mProfile.add("temptargetSet", tempTargetSet);
mProfile.add("autosens_adjust_targets", SP.getBoolean("openapsama_autosens_adjusttargets", true));
mProfile.add("min_5m_carbimpact", SP.getDouble("openapsama_min_5m_carbimpact", 3d));
mProfile.add("enableSMB_with_bolus", SP.getBoolean(R.string.key_use_smb, false));
mProfile.add("enableSMB_with_COB", SP.getBoolean(R.string.key_use_smb, false));
mProfile.add("enableSMB_with_temptarget", SP.getBoolean(R.string.key_use_smb, false));
mProfile.add("enableUAM", SP.getBoolean(R.string.key_use_uam, false));
mProfile.add("adv_target_adjustments", true); // lower target automatically when BG and eventualBG are high
// create maxCOB and default it to 120 because that's the most a typical body can absorb over 4 hours.
// (If someone enters more carbs or stacks more; OpenAPS will just truncate dosing based on 120.
// Essentially, this just limits AMA as a safety cap against weird COB calculations)
mProfile.add("maxCOB", 120);
mProfile.add("autotune_isf_adjustmentFraction", 0.5); // keep autotune ISF closer to pump ISF via a weighted average of fullNewISF and pumpISF. 1.0 allows full adjustment, 0 is no adjustment from pump ISF.
mProfile.add("remainingCarbsFraction", 1.0d); // fraction of carbs we'll assume will absorb over 4h if we don't yet see carb absorption
mProfile.add("remainingCarbsCap", 90); // max carbs we'll assume will absorb over 4h if we don't yet see carb absorption
mV8rt.add(PARAM_profile, mProfile);
mCurrentTemp = new V8Object(mV8rt);
mCurrentTemp.add("temp", "absolute");
mCurrentTemp.add("duration", MainApp.getConfigBuilder().getTempBasalRemainingMinutesFromHistory());
mCurrentTemp.add("rate", MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory());
// as we have non default temps longer than 30 mintues
TemporaryBasal tempBasal = MainApp.getConfigBuilder().getTempBasalFromHistory(System.currentTimeMillis());
if (tempBasal != null) {
mCurrentTemp.add("minutesrunning", tempBasal.getRealDuration());
}
mV8rt.add(PARAM_currentTemp, mCurrentTemp);
mIobData = mV8rt.executeArrayScript(IobCobCalculatorPlugin.convertToJSONArray(iobArray).toString());
mV8rt.add(PARAM_iobData, mIobData);
mGlucoseStatus = new V8Object(mV8rt);
mGlucoseStatus.add("glucose", glucoseStatus.glucose);
if (SP.getBoolean("always_use_shortavg", false)) {
mGlucoseStatus.add("delta", glucoseStatus.short_avgdelta);
} else {
mGlucoseStatus.add("delta", glucoseStatus.delta);
}
mGlucoseStatus.add("short_avgdelta", glucoseStatus.short_avgdelta);
mGlucoseStatus.add("long_avgdelta", glucoseStatus.long_avgdelta);
mV8rt.add(PARAM_glucoseStatus, mGlucoseStatus);
mMealData = new V8Object(mV8rt);
mMealData.add("carbs", mealData.carbs);
mMealData.add("boluses", mealData.boluses);
mMealData.add("mealCOB", mealData.mealCOB);
mMealData.add("minDeviationSlope", mealData.minDeviationSlope);
mV8rt.add(PARAM_meal_data, mMealData);
if (MainApp.getConfigBuilder().isAMAModeEnabled()) {
mAutosensData = new V8Object(mV8rt);
mAutosensData.add("ratio", autosensDataRatio);
mV8rt.add(PARAM_autosens_data, mAutosensData);
} else {
mV8rt.addUndefined(PARAM_autosens_data);
}
mV8rt.addUndefined(PARAM_reservoirData);
mV8rt.add(PARAM_microBolusAllowed, microBolusAllowed);
}
public void release() {
mProfile.release();
mCurrentTemp.release();
mIobData.release();
mMealData.release();
mGlucoseStatus.release();
if (mAutosensData != null) {
mAutosensData.release();
}
mV8rt.release();
}
public String readFile(String filename) throws IOException {
byte[] bytes = mScriptReader.readFile(filename);
String string = new String(bytes, "UTF-8");
if (string.startsWith("#!/usr/bin/env node")) {
string = string.substring(20);
}
return string;
}
}

View file

@ -0,0 +1,180 @@
package info.nightscout.androidaps.plugins.OpenAPSSMB;
import com.eclipsesource.v8.V8Object;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.db.BgReading;
import info.nightscout.androidaps.plugins.Loop.APSResult;
import info.nightscout.utils.DateUtil;
public class DetermineBasalResultSMB extends APSResult {
public Date date;
public JSONObject json = new JSONObject();
public double eventualBG;
public double snoozeBG;
public IobTotal iob;
public double smbValue;
public long deliverAt;
public DetermineBasalResultSMB(JSONObject result) {
date = new Date();
json = result;
try {
if (result.has("error")) {
reason = result.getString("error");
changeRequested = false;
rate = -1;
duration = -1;
} else {
reason = result.getString("reason");
if (result.has("eventualBG")) eventualBG = result.getDouble("eventualBG");
if (result.has("snoozeBG")) snoozeBG = result.getDouble("snoozeBG");
if (result.has("rate")) {
rate = result.getDouble("rate");
if (rate < 0d) rate = 0d;
changeRequested = true;
} else {
rate = -1;
changeRequested = false;
}
if (result.has("duration")) {
duration = result.getInt("duration");
//changeRequested as above
} else {
duration = -1;
changeRequested = false;
}
if (result.has("units")) {
changeRequested = true;
smbValue = result.getDouble("units");
} else {
smbValue = 0.0;
//changeRequested as above
}
if (result.has("deliverAt")) {
String date = result.getString("deliverAt");
try {
deliverAt = DateUtil.fromISODateString(date).getTime();
} catch (Exception e) {
e.printStackTrace();
}
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
public DetermineBasalResultSMB() {
}
@Override
public DetermineBasalResultSMB clone() {
DetermineBasalResultSMB newResult = new DetermineBasalResultSMB();
newResult.reason = new String(reason);
newResult.rate = rate;
newResult.duration = duration;
newResult.changeRequested = changeRequested;
newResult.rate = rate;
newResult.duration = duration;
newResult.smbValue = smbValue;
try {
newResult.json = new JSONObject(json.toString());
} catch (JSONException e) {
e.printStackTrace();
}
newResult.eventualBG = eventualBG;
newResult.snoozeBG = snoozeBG;
newResult.date = date;
return newResult;
}
@Override
public JSONObject json() {
try {
JSONObject ret = new JSONObject(this.json.toString());
return ret;
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
public List<BgReading> getPredictions() {
List<BgReading> array = new ArrayList<>();
try {
long startTime = date.getTime();
if (json.has("predBGs")) {
JSONObject predBGs = json.getJSONObject("predBGs");
if (predBGs.has("IOB")) {
JSONArray iob = predBGs.getJSONArray("IOB");
for (int i = 1; i < iob.length(); i++) {
BgReading bg = new BgReading();
bg.value = iob.getInt(i);
bg.date = startTime + i * 5 * 60 * 1000L;
bg.isPrediction = true;
array.add(bg);
}
}
if (predBGs.has("aCOB")) {
JSONArray iob = predBGs.getJSONArray("aCOB");
for (int i = 1; i < iob.length(); i++) {
BgReading bg = new BgReading();
bg.value = iob.getInt(i);
bg.date = startTime + i * 5 * 60 * 1000L;
bg.isPrediction = true;
array.add(bg);
}
}
if (predBGs.has("COB")) {
JSONArray iob = predBGs.getJSONArray("COB");
for (int i = 1; i < iob.length(); i++) {
BgReading bg = new BgReading();
bg.value = iob.getInt(i);
bg.date = startTime + i * 5 * 60 * 1000L;
bg.isPrediction = true;
array.add(bg);
}
}
}
} catch (JSONException e) {
e.printStackTrace();
}
return array;
}
public long getLatestPredictionsTime() {
long latest = 0;
try {
long startTime = date.getTime();
if (json.has("predBGs")) {
JSONObject predBGs = json.getJSONObject("predBGs");
if (predBGs.has("IOB")) {
JSONArray iob = predBGs.getJSONArray("IOB");
latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L);
}
if (predBGs.has("aCOB")) {
JSONArray iob = predBGs.getJSONArray("aCOB");
latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L);
}
if (predBGs.has("COB")) {
JSONArray iob = predBGs.getJSONArray("COB");
latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L);
}
}
} catch (JSONException e) {
e.printStackTrace();
}
return latest;
}
}

View file

@ -0,0 +1,150 @@
package info.nightscout.androidaps.plugins.OpenAPSSMB;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import com.crashlytics.android.answers.Answers;
import com.crashlytics.android.answers.CustomEvent;
import com.squareup.otto.Subscribe;
import org.json.JSONArray;
import org.json.JSONException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.Common.SubscriberFragment;
import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateGui;
import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateResultGui;
import info.nightscout.utils.JSONFormatter;
public class OpenAPSSMBFragment extends SubscriberFragment implements View.OnClickListener {
private static Logger log = LoggerFactory.getLogger(OpenAPSSMBFragment.class);
private static OpenAPSSMBPlugin openAPSSMBPlugin;
public static OpenAPSSMBPlugin getPlugin() {
if (openAPSSMBPlugin == null) {
openAPSSMBPlugin = new OpenAPSSMBPlugin();
}
return openAPSSMBPlugin;
}
Button run;
TextView lastRunView;
TextView glucoseStatusView;
TextView currentTempView;
TextView iobDataView;
TextView profileView;
TextView mealDataView;
TextView autosensDataView;
TextView resultView;
TextView scriptdebugView;
TextView requestView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.openapsama_fragment, container, false);
run = (Button) view.findViewById(R.id.openapsma_run);
run.setOnClickListener(this);
lastRunView = (TextView) view.findViewById(R.id.openapsma_lastrun);
glucoseStatusView = (TextView) view.findViewById(R.id.openapsma_glucosestatus);
currentTempView = (TextView) view.findViewById(R.id.openapsma_currenttemp);
iobDataView = (TextView) view.findViewById(R.id.openapsma_iobdata);
profileView = (TextView) view.findViewById(R.id.openapsma_profile);
mealDataView = (TextView) view.findViewById(R.id.openapsma_mealdata);
autosensDataView = (TextView) view.findViewById(R.id.openapsma_autosensdata);
scriptdebugView = (TextView) view.findViewById(R.id.openapsma_scriptdebugdata);
resultView = (TextView) view.findViewById(R.id.openapsma_result);
requestView = (TextView) view.findViewById(R.id.openapsma_request);
updateGUI();
return view;
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.openapsma_run:
getPlugin().invoke("OpenAPSAMA button");
Answers.getInstance().logCustom(new CustomEvent("OpenAPS_AMA_Run"));
break;
}
}
@Subscribe
public void onStatusEvent(final EventOpenAPSUpdateGui ev) {
updateGUI();
}
@Subscribe
public void onStatusEvent(final EventOpenAPSUpdateResultGui ev) {
updateResultGUI(ev.text);
}
@Override
protected void updateGUI() {
Activity activity = getActivity();
if (activity != null)
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
DetermineBasalResultSMB lastAPSResult = getPlugin().lastAPSResult;
if (lastAPSResult != null) {
resultView.setText(JSONFormatter.format(lastAPSResult.json));
requestView.setText(lastAPSResult.toSpanned());
}
DetermineBasalAdapterSMBJS determineBasalAdapterSMBJS = getPlugin().lastDetermineBasalAdapterSMBJS;
if (determineBasalAdapterSMBJS != null) {
glucoseStatusView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getGlucoseStatusParam()));
currentTempView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getCurrentTempParam()));
try {
JSONArray iobArray = new JSONArray(determineBasalAdapterSMBJS.getIobDataParam());
iobDataView.setText(String.format(MainApp.sResources.getString(R.string.array_of_elements), iobArray.length()) + "\n" + JSONFormatter.format(iobArray.getString(0)));
} catch (JSONException e) {
e.printStackTrace();
iobDataView.setText("JSONException");
}
profileView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getProfileParam()));
mealDataView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getMealDataParam()));
scriptdebugView.setText(determineBasalAdapterSMBJS.getScriptDebug());
}
if (getPlugin().lastAPSRun != null) {
lastRunView.setText(getPlugin().lastAPSRun.toLocaleString());
}
if (getPlugin().lastAutosensResult != null) {
autosensDataView.setText(JSONFormatter.format(getPlugin().lastAutosensResult.json()));
}
}
});
}
void updateResultGUI(final String text) {
Activity activity = getActivity();
if (activity != null)
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
resultView.setText(text);
glucoseStatusView.setText("");
currentTempView.setText("");
iobDataView.setText("");
profileView.setText("");
mealDataView.setText("");
autosensDataView.setText("");
scriptdebugView.setText("");
requestView.setText("");
lastRunView.setText("");
}
});
}
}

View file

@ -0,0 +1,279 @@
package info.nightscout.androidaps.plugins.OpenAPSSMB;
import org.json.JSONException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Date;
import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.GlucoseStatus;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.MealData;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.db.TempTarget;
import info.nightscout.androidaps.interfaces.APSInterface;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult;
import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.plugins.Loop.APSResult;
import info.nightscout.androidaps.plugins.Loop.ScriptReader;
import info.nightscout.androidaps.plugins.OpenAPSSMB.DetermineBasalResultSMB;
import info.nightscout.androidaps.plugins.OpenAPSSMB.DetermineBasalAdapterSMBJS;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateGui;
import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateResultGui;
import info.nightscout.utils.DateUtil;
import info.nightscout.utils.NSUpload;
import info.nightscout.utils.Profiler;
import info.nightscout.utils.Round;
import info.nightscout.utils.SP;
import info.nightscout.utils.SafeParse;
import info.nightscout.utils.ToastUtils;
/**
* Created by mike on 05.08.2016.
*/
public class OpenAPSSMBPlugin implements PluginBase, APSInterface {
private static Logger log = LoggerFactory.getLogger(OpenAPSSMBPlugin.class);
// last values
DetermineBasalAdapterSMBJS lastDetermineBasalAdapterSMBJS = null;
Date lastAPSRun = null;
DetermineBasalResultSMB lastAPSResult = null;
AutosensResult lastAutosensResult = null;
boolean fragmentEnabled = false;
boolean fragmentVisible = true;
@Override
public String getName() {
return MainApp.instance().getString(R.string.openapssmb);
}
@Override
public String getNameShort() {
String name = MainApp.sResources.getString(R.string.smb_shortname);
if (!name.trim().isEmpty()){
//only if translation exists
return name;
}
// use long name as fallback
return getName();
}
@Override
public boolean isEnabled(int type) {
return type == APS && fragmentEnabled && MainApp.getConfigBuilder().getPumpDescription().isTempBasalCapable;
}
@Override
public boolean isVisibleInTabs(int type) {
return type == APS && fragmentVisible && MainApp.getConfigBuilder().getPumpDescription().isTempBasalCapable;
}
@Override
public boolean canBeHidden(int type) {
return true;
}
@Override
public boolean hasFragment() {
return true;
}
@Override
public boolean showInList(int type) {
return true;
}
@Override
public void setFragmentVisible(int type, boolean fragmentVisible) {
if (type == APS) this.fragmentVisible = fragmentVisible;
}
@Override
public void setFragmentEnabled(int type, boolean fragmentEnabled) {
if (type == APS) this.fragmentEnabled = fragmentEnabled;
}
@Override
public int getType() {
return PluginBase.APS;
}
@Override
public String getFragmentClass() {
return OpenAPSSMBFragment.class.getName();
}
@Override
public APSResult getLastAPSResult() {
return lastAPSResult;
}
@Override
public Date getLastAPSRun() {
return lastAPSRun;
}
@Override
public void invoke(String initiator) {
log.debug("invoke from " + initiator);
lastAPSResult = null;
DetermineBasalAdapterSMBJS determineBasalAdapterSMBJS = null;
try {
determineBasalAdapterSMBJS = new DetermineBasalAdapterSMBJS(new ScriptReader(MainApp.instance().getBaseContext()));
} catch (IOException e) {
log.error(e.getMessage(), e);
return;
}
GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData();
Profile profile = MainApp.getConfigBuilder().getProfile();
PumpInterface pump = MainApp.getConfigBuilder();
if (profile == null) {
MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.instance().getString(R.string.noprofileselected)));
if (Config.logAPSResult)
log.debug(MainApp.instance().getString(R.string.noprofileselected));
return;
}
if (!isEnabled(PluginBase.APS)) {
MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.instance().getString(R.string.openapsma_disabled)));
if (Config.logAPSResult)
log.debug(MainApp.instance().getString(R.string.openapsma_disabled));
return;
}
if (glucoseStatus == null) {
MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.instance().getString(R.string.openapsma_noglucosedata)));
if (Config.logAPSResult)
log.debug(MainApp.instance().getString(R.string.openapsma_noglucosedata));
return;
}
String units = profile.getUnits();
double maxIob = SP.getDouble("openapsma_max_iob", 1.5d);
double maxBasal = SP.getDouble("openapsma_max_basal", 1d);
double minBg = Profile.toMgdl(profile.getTargetLow(), units);
double maxBg = Profile.toMgdl(profile.getTargetHigh(), units);
double targetBg = (minBg + maxBg) / 2;
minBg = Round.roundTo(minBg, 0.1d);
maxBg = Round.roundTo(maxBg, 0.1d);
Date start = new Date();
Date startPart = new Date();
IobTotal[] iobArray = IobCobCalculatorPlugin.calculateIobArrayInDia();
Profiler.log(log, "calculateIobArrayInDia()", startPart);
startPart = new Date();
MealData mealData = MainApp.getConfigBuilder().getMealData();
Profiler.log(log, "getMealData()", startPart);
maxIob = MainApp.getConfigBuilder().applyMaxIOBConstraints(maxIob);
minBg = verifyHardLimits(minBg, "minBg", Constants.VERY_HARD_LIMIT_MIN_BG[0], Constants.VERY_HARD_LIMIT_MIN_BG[1]);
maxBg = verifyHardLimits(maxBg, "maxBg", Constants.VERY_HARD_LIMIT_MAX_BG[0], Constants.VERY_HARD_LIMIT_MAX_BG[1]);
targetBg = verifyHardLimits(targetBg, "targetBg", Constants.VERY_HARD_LIMIT_TARGET_BG[0], Constants.VERY_HARD_LIMIT_TARGET_BG[1]);
boolean isTempTarget = false;
TempTarget tempTarget = MainApp.getConfigBuilder().getTempTargetFromHistory(System.currentTimeMillis());
if (tempTarget != null) {
isTempTarget = true;
minBg = verifyHardLimits(tempTarget.low, "minBg", Constants.VERY_HARD_LIMIT_TEMP_MIN_BG[0], Constants.VERY_HARD_LIMIT_TEMP_MIN_BG[1]);
maxBg = verifyHardLimits(tempTarget.high, "maxBg", Constants.VERY_HARD_LIMIT_TEMP_MAX_BG[0], Constants.VERY_HARD_LIMIT_TEMP_MAX_BG[1]);
targetBg = verifyHardLimits((tempTarget.low + tempTarget.high) / 2, "targetBg", Constants.VERY_HARD_LIMIT_TEMP_TARGET_BG[0], Constants.VERY_HARD_LIMIT_TEMP_TARGET_BG[1]);
}
maxIob = verifyHardLimits(maxIob, "maxIob", 0, 7);
maxBasal = verifyHardLimits(maxBasal, "max_basal", 0.1, 10);
if (!checkOnlyHardLimits(profile.getDia(), "dia", 2, 7)) return;
if (!checkOnlyHardLimits(profile.getIc(profile.secondsFromMidnight()), "carbratio", 2, 100))
return;
if (!checkOnlyHardLimits(Profile.toMgdl(profile.getIsf().doubleValue(), units), "sens", 2, 900))
return;
if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.1, 10)) return;
if (!checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, 5)) return;
startPart = new Date();
if (MainApp.getConfigBuilder().isAMAModeEnabled()) {
lastAutosensResult = IobCobCalculatorPlugin.detectSensitivityWithLock(IobCobCalculatorPlugin.oldestDataAvailable(), System.currentTimeMillis());
} else {
lastAutosensResult = new AutosensResult();
}
Profiler.log(log, "detectSensitivityandCarbAbsorption()", startPart);
Profiler.log(log, "AMA data gathering", start);
start = new Date();
determineBasalAdapterSMBJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, pump, iobArray, glucoseStatus, mealData,
lastAutosensResult.ratio, //autosensDataRatio
isTempTarget,
true //microBolusAllowed
);
DetermineBasalResultSMB determineBasalResultSMB = determineBasalAdapterSMBJS.invoke();
Profiler.log(log, "SMB calculation", start);
// Fix bug determine basal
if (determineBasalResultSMB.rate == 0d && determineBasalResultSMB.duration == 0 && !MainApp.getConfigBuilder().isTempBasalInProgress())
determineBasalResultSMB.changeRequested = false;
// limit requests on openloop mode
if (!MainApp.getConfigBuilder().isClosedModeEnabled()) {
if (MainApp.getConfigBuilder().isTempBasalInProgress() && Math.abs(determineBasalResultSMB.rate - MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory()) < 0.1)
determineBasalResultSMB.changeRequested = false;
if (!MainApp.getConfigBuilder().isTempBasalInProgress() && Math.abs(determineBasalResultSMB.rate - MainApp.getConfigBuilder().getBaseBasalRate()) < 0.1)
determineBasalResultSMB.changeRequested = false;
}
determineBasalResultSMB.iob = iobArray[0];
determineBasalAdapterSMBJS.release();
Date now = new Date();
try {
determineBasalResultSMB.json.put("timestamp", DateUtil.toISOString(now));
} catch (JSONException e) {
e.printStackTrace();
}
lastDetermineBasalAdapterSMBJS = determineBasalAdapterSMBJS;
lastAPSResult = determineBasalResultSMB;
lastAPSRun = now;
MainApp.bus().post(new EventOpenAPSUpdateGui());
//deviceStatus.suggested = determineBasalResultAMA.json;
}
// safety checks
public static boolean checkOnlyHardLimits(Double value, String valueName, double lowLimit, double highLimit) {
return value.equals(verifyHardLimits(value, valueName, lowLimit, highLimit));
}
public static Double verifyHardLimits(Double value, String valueName, double lowLimit, double highLimit) {
Double newvalue = value;
if (newvalue < lowLimit || newvalue > highLimit) {
newvalue = Math.max(newvalue, lowLimit);
newvalue = Math.min(newvalue, highLimit);
String msg = String.format(MainApp.sResources.getString(R.string.openapsma_valueoutofrange), valueName);
msg += ".\n";
msg += String.format(MainApp.sResources.getString(R.string.openapsma_valuelimitedto), value, newvalue);
log.error(msg);
NSUpload.uploadError(msg);
ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), msg, R.raw.error);
}
return newvalue;
}
}

View file

@ -187,6 +187,8 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface {
Iob bIOB = t.iobCalc(time, dia / SP.getDouble("openapsama_bolussnooze_dia_divisor", 2.0));
total.bolussnooze += bIOB.iobContrib;
} else {
if (t.date > total.lastBolusTime)
total.lastBolusTime = t.date;
total.basaliob += t.insulin;
total.microBolusIOB += tIOB.iobContrib;
}
@ -236,6 +238,7 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface {
AutosensData autosensData = IobCobCalculatorPlugin.getLastAutosensData();
if (autosensData != null) {
result.mealCOB = autosensData.cob;
result.minDeviationSlope = autosensData.minDeviationSlope;
}
return result;
}

View file

@ -689,5 +689,13 @@
<string name="careportal_pump_label">PUMP</string>
<string name="overview_newtempbasal_basalabsolute">Basal value [U/h]</string>
<string name="careportal_newnstreatment_duration_min_label">Duration [min]</string>
<string name="openapssmb">OpenAPS SMB</string>
<string name="smb_shortname">SMB</string>
<string name="key_use_smb">use_smb</string>
<string name="key_use_uam">use_uam</string>
<string name="enableuam">Enable UAM</string>
<string name="enablesmb">Enable SMB</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>
</resources>

View file

@ -4,125 +4,130 @@
<PreferenceCategory
android:key="advanced"
android:title="@string/advancedsettings_title">
<PreferenceScreen
android:title="@string/advancedsettings_title">
<PreferenceCategory
android:title="@string/nightscout">
<PreferenceScreen android:title="@string/advancedsettings_title">
<PreferenceCategory android:title="@string/nightscout">
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_ns_upload_only"
android:title="@string/ns_upload_only"
android:summary="@string/ns_upload_only_summary"/>
android:summary="@string/ns_upload_only_summary"
android:title="@string/ns_upload_only" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_ns_noupload"
android:title="@string/ns_noupload"
android:summary="@string/ns_noupload_summary"/>
android:summary="@string/ns_noupload_summary"
android:title="@string/ns_noupload" />
<SwitchPreference
android:defaultValue="false"
android:key="ns_sync_use_absolute"
android:title="@string/ns_sync_use_absolute_title" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/superbolus">
<PreferenceCategory android:title="@string/superbolus">
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_usesuperbolus"
android:title="@string/enablesuperbolus"
android:summary="@string/enablesuperbolus_summary"/>
android:summary="@string/enablesuperbolus_summary"
android:title="@string/enablesuperbolus" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/openapsma">
<PreferenceCategory android:title="@string/openapsma">
<SwitchPreference
android:defaultValue="false"
android:key="always_use_shortavg"
android:title="@string/always_use_shortavg"
android:summary="@string/always_use_shortavg_summary"/>
android:summary="@string/always_use_shortavg_summary"
android:title="@string/always_use_shortavg" />
</PreferenceCategory>
<PreferenceCategory
android:title="OpenAPS preferences.json">
<Preference
android:summary="@string/openapsama_link_to_preferncejson_doc_txt" >
<PreferenceCategory android:title="@string/openapssmb">
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_use_smb"
android:summary="@string/enablesmb_summary"
android:title="@string/enablesmb" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_use_uam"
android:summary="@string/enableuam_summary"
android:title="@string/enableuam" />
</PreferenceCategory>
<PreferenceCategory android:title="OpenAPS preferences.json">
<Preference android:summary="@string/openapsama_link_to_preferncejson_doc_txt">
<intent
android:action="android.intent.action.VIEW"
android:data="@string/openapsama_link_to_preferncejson_doc" />
</Preference>
<com.andreabaccega.widget.ValidatingEditTextPreference
validate:testType="numericRange"
validate:minNumber="1"
validate:maxNumber="10"
android:digits="0123456789.,"
android:defaultValue="3"
android:selectAllOnFocus="true"
android:inputType="number"
android:maxLines="20"
android:title="@string/openapsama_max_daily_safety_multiplier"
android:dialogMessage="@string/openapsama_max_daily_safety_multiplier_summary"
android:key="openapsama_max_daily_safety_multiplier" />
<com.andreabaccega.widget.ValidatingEditTextPreference
validate:testType="numericRange"
validate:minNumber="1"
validate:maxNumber="10"
android:digits="0123456789.,"
android:defaultValue="4"
android:selectAllOnFocus="true"
android:singleLine="true"
android:inputType="number"
android:key="openapsama_max_daily_safety_multiplier"
android:maxLines="20"
android:title="@string/openapsama_current_basal_safety_multiplier"
android:selectAllOnFocus="true"
android:title="@string/openapsama_max_daily_safety_multiplier"
validate:maxNumber="10"
validate:minNumber="1"
validate:testType="numericRange" />
<com.andreabaccega.widget.ValidatingEditTextPreference
android:defaultValue="4"
android:dialogMessage="@string/openapsama_current_basal_safety_multiplier_summary"
android:key="openapsama_current_basal_safety_multiplier" />
<com.andreabaccega.widget.ValidatingEditTextPreference
validate:testType="floatNumericRange"
validate:floatminNumber="0.5"
validate:floatmaxNumber="3"
android:digits="0123456789.,"
android:defaultValue="1.2"
android:inputType="number"
android:key="openapsama_current_basal_safety_multiplier"
android:maxLines="20"
android:selectAllOnFocus="true"
android:singleLine="true"
android:inputType="numberDecimal"
android:maxLines="20"
android:title="@string/openapsama_autosens_max"
android:dialogMessage="@string/openapsama_autosens_max_summary"
android:key="openapsama_autosens_max" />
android:title="@string/openapsama_current_basal_safety_multiplier"
validate:maxNumber="10"
validate:minNumber="1"
validate:testType="numericRange" />
<com.andreabaccega.widget.ValidatingEditTextPreference
validate:testType="floatNumericRange"
validate:floatminNumber="0.1"
validate:floatmaxNumber="1.0"
android:defaultValue="0.7"
android:defaultValue="1.2"
android:dialogMessage="@string/openapsama_autosens_max_summary"
android:digits="0123456789.,"
android:inputType="numberDecimal"
android:key="openapsama_autosens_max"
android:maxLines="20"
android:selectAllOnFocus="true"
android:singleLine="true"
android:inputType="numberDecimal"
android:maxLines="20"
android:title="@string/openapsama_autosens_min"
android:title="@string/openapsama_autosens_max"
validate:floatmaxNumber="3"
validate:floatminNumber="0.5"
validate:testType="floatNumericRange" />
<com.andreabaccega.widget.ValidatingEditTextPreference
android:defaultValue="0.7"
android:dialogMessage="@string/openapsama_autosens_min_summary"
android:key="openapsama_autosens_min" />
android:inputType="numberDecimal"
android:key="openapsama_autosens_min"
android:maxLines="20"
android:selectAllOnFocus="true"
android:singleLine="true"
android:title="@string/openapsama_autosens_min"
validate:floatmaxNumber="1.0"
validate:floatminNumber="0.1"
validate:testType="floatNumericRange" />
<SwitchPreference
android:defaultValue="true"
android:key="openapsama_autosens_adjusttargets"
android:title="@string/openapsama_autosens_adjusttargets"
android:summary="@string/openapsama_autosens_adjusttargets_summary"/>
android:summary="@string/openapsama_autosens_adjusttargets_summary"
android:title="@string/openapsama_autosens_adjusttargets" />
<com.andreabaccega.widget.ValidatingEditTextPreference
validate:testType="floatNumericRange"
validate:minNumber="1"
validate:maxNumber="10"
android:digits="0123456789.,"
android:defaultValue="2"
android:dialogMessage="@string/openapsama_bolussnooze_dia_divisor_summary"
android:digits="0123456789.,"
android:inputType="numberDecimal"
android:key="openapsama_bolussnooze_dia_divisor"
android:maxLines="20"
android:selectAllOnFocus="true"
android:singleLine="true"
android:inputType="numberDecimal"
android:maxLines="20"
android:title="@string/openapsama_bolussnooze_dia_divisor"
android:dialogMessage="@string/openapsama_bolussnooze_dia_divisor_summary"
android:key="openapsama_bolussnooze_dia_divisor" />
validate:maxNumber="10"
validate:minNumber="1"
validate:testType="floatNumericRange" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/profile">
<PreferenceCategory android:title="@string/profile">
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_do_not_track_profile_switch"
android:title="@string/do_not_track_profile_switch"
android:summary="@string/do_not_track_profile_switch_summary"/>
android:summary="@string/do_not_track_profile_switch_summary"
android:title="@string/do_not_track_profile_switch" />
</PreferenceCategory>
</PreferenceScreen>
</PreferenceCategory>