commit
411add616b
25 changed files with 2531 additions and 0 deletions
|
@ -200,6 +200,8 @@ dependencies {
|
||||||
compile "com.joanzapata.iconify:android-iconify-fontawesome:2.1.1"
|
compile "com.joanzapata.iconify:android-iconify-fontawesome:2.1.1"
|
||||||
compile "com.google.android.gms:play-services-wearable:7.5.0"
|
compile "com.google.android.gms:play-services-wearable:7.5.0"
|
||||||
compile(name: "android-edittext-validator-v1.3.4-mod", ext: "aar")
|
compile(name: "android-edittext-validator-v1.3.4-mod", ext: "aar")
|
||||||
|
compile(name: "sightparser-release", ext: "aar")
|
||||||
|
|
||||||
compile("com.google.android:flexbox:0.3.0") {
|
compile("com.google.android:flexbox:0.3.0") {
|
||||||
exclude group: "com.android.support"
|
exclude group: "com.android.support"
|
||||||
}
|
}
|
||||||
|
@ -223,6 +225,7 @@ dependencies {
|
||||||
testCompile "org.powermock:powermock-module-junit4-rule:${powermockVersion}"
|
testCompile "org.powermock:powermock-module-junit4-rule:${powermockVersion}"
|
||||||
testCompile "org.powermock:powermock-module-junit4:${powermockVersion}"
|
testCompile "org.powermock:powermock-module-junit4:${powermockVersion}"
|
||||||
testCompile "joda-time:joda-time:2.9.4.2"
|
testCompile "joda-time:joda-time:2.9.4.2"
|
||||||
|
testCompile "com.google.truth:truth:0.39"
|
||||||
|
|
||||||
androidTestCompile "org.mockito:mockito-core:2.7.22"
|
androidTestCompile "org.mockito:mockito-core:2.7.22"
|
||||||
androidTestCompile "com.google.dexmaker:dexmaker:${dexmakerVersion}"
|
androidTestCompile "com.google.dexmaker:dexmaker:${dexmakerVersion}"
|
||||||
|
|
BIN
app/libs/sightparser-release.aar
Normal file
BIN
app/libs/sightparser-release.aar
Normal file
Binary file not shown.
|
@ -54,6 +54,7 @@ import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPlugin;
|
||||||
import info.nightscout.androidaps.plugins.PumpDanaRKorean.DanaRKoreanPlugin;
|
import info.nightscout.androidaps.plugins.PumpDanaRKorean.DanaRKoreanPlugin;
|
||||||
import info.nightscout.androidaps.plugins.PumpDanaRS.DanaRSPlugin;
|
import info.nightscout.androidaps.plugins.PumpDanaRS.DanaRSPlugin;
|
||||||
import info.nightscout.androidaps.plugins.PumpDanaRv2.DanaRv2Plugin;
|
import info.nightscout.androidaps.plugins.PumpDanaRv2.DanaRv2Plugin;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.InsightPumpPlugin;
|
||||||
import info.nightscout.androidaps.plugins.PumpMDI.MDIPlugin;
|
import info.nightscout.androidaps.plugins.PumpMDI.MDIPlugin;
|
||||||
import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin;
|
import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin;
|
||||||
import info.nightscout.androidaps.plugins.SensitivityAAPS.SensitivityAAPSPlugin;
|
import info.nightscout.androidaps.plugins.SensitivityAAPS.SensitivityAAPSPlugin;
|
||||||
|
@ -129,6 +130,7 @@ public class MainApp extends Application {
|
||||||
if (Config.DANAR) pluginsList.add(DanaRv2Plugin.getPlugin());
|
if (Config.DANAR) pluginsList.add(DanaRv2Plugin.getPlugin());
|
||||||
if (Config.DANAR) pluginsList.add(DanaRSPlugin.getPlugin());
|
if (Config.DANAR) pluginsList.add(DanaRSPlugin.getPlugin());
|
||||||
pluginsList.add(CareportalPlugin.getPlugin());
|
pluginsList.add(CareportalPlugin.getPlugin());
|
||||||
|
if (Config.DANAR) pluginsList.add(InsightPumpPlugin.getPlugin());
|
||||||
if (Config.MDI) pluginsList.add(MDIPlugin.getPlugin());
|
if (Config.MDI) pluginsList.add(MDIPlugin.getPlugin());
|
||||||
if (Config.VIRTUALPUMP) pluginsList.add(VirtualPumpPlugin.getPlugin());
|
if (Config.VIRTUALPUMP) pluginsList.add(VirtualPumpPlugin.getPlugin());
|
||||||
if (Config.APS) pluginsList.add(LoopPlugin.getPlugin());
|
if (Config.APS) pluginsList.add(LoopPlugin.getPlugin());
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by jamorham on 25/01/2018.
|
||||||
|
*
|
||||||
|
* Async command status
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
enum Cstatus {
|
||||||
|
UNKNOWN,
|
||||||
|
PENDING,
|
||||||
|
SUCCESS,
|
||||||
|
FAILURE,
|
||||||
|
TIMEOUT;
|
||||||
|
|
||||||
|
boolean success() {
|
||||||
|
return this == SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight;
|
||||||
|
|
||||||
|
import android.os.PowerManager;
|
||||||
|
|
||||||
|
import com.squareup.otto.Subscribe;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.events.EventInsightPumpCallback;
|
||||||
|
|
||||||
|
import static info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers.getWakeLock;
|
||||||
|
import static info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers.msSince;
|
||||||
|
import static info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers.releaseWakeLock;
|
||||||
|
import static info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers.tsl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by jamorham on 25/01/2018.
|
||||||
|
*
|
||||||
|
* Asynchronous adapter
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class InsightPumpAsyncAdapter {
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<UUID, EventInsightPumpCallback> commandResults = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
InsightPumpAsyncAdapter() {
|
||||||
|
MainApp.bus().register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// just log during debugging
|
||||||
|
private static void log(String msg) {
|
||||||
|
android.util.Log.e("INSIGHTPUMPASYNC", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onStatusEvent(final EventInsightPumpCallback ev) {
|
||||||
|
log("Received callback event: " + ev.toString());
|
||||||
|
commandResults.put(ev.request_uuid, ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
// poll command result
|
||||||
|
private Cstatus checkCommandResult(UUID uuid) {
|
||||||
|
if (uuid == null) return Cstatus.FAILURE;
|
||||||
|
if (commandResults.containsKey(uuid)) {
|
||||||
|
if (commandResults.get(uuid).success) {
|
||||||
|
return Cstatus.SUCCESS;
|
||||||
|
} else {
|
||||||
|
return Cstatus.FAILURE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Cstatus.PENDING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// blocking call to wait for result callback
|
||||||
|
Cstatus busyWaitForCommandResult(final UUID uuid, long wait_time) {
|
||||||
|
final PowerManager.WakeLock wl = getWakeLock("insight-wait-cmd", 60000);
|
||||||
|
try {
|
||||||
|
log("busy wait for command " + uuid);
|
||||||
|
if (uuid == null) return Cstatus.FAILURE;
|
||||||
|
final long start_time = tsl();
|
||||||
|
Cstatus status = checkCommandResult(uuid);
|
||||||
|
while ((status == Cstatus.PENDING) && msSince(start_time) < wait_time) {
|
||||||
|
//log("command result waiting");
|
||||||
|
try {
|
||||||
|
Thread.sleep(200);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log("Got interrupted exception! " + e);
|
||||||
|
}
|
||||||
|
status = checkCommandResult(uuid);
|
||||||
|
}
|
||||||
|
if (status == Cstatus.PENDING) {
|
||||||
|
return Cstatus.TIMEOUT;
|
||||||
|
} else {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
releaseWakeLock(wl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// commend field preparation for results
|
||||||
|
String getCommandComment(final UUID uuid) {
|
||||||
|
if (commandResults.containsKey(uuid)) {
|
||||||
|
if (commandResults.get(uuid).success) {
|
||||||
|
return "OK";
|
||||||
|
} else {
|
||||||
|
return commandResults.get(uuid).message;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "Unknown reference";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getResponseID(UUID uuid) {
|
||||||
|
if (checkCommandResult(uuid) == Cstatus.SUCCESS) {
|
||||||
|
return commandResults.get(uuid).response_id;
|
||||||
|
} else {
|
||||||
|
return -2; // invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import com.crashlytics.android.Crashlytics;
|
||||||
|
import com.squareup.otto.Subscribe;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.plugins.Common.SubscriberFragment;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.events.EventInsightPumpUpdateGui;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.utils.StatusItem;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.utils.ui.StatusItemViewAdapter;
|
||||||
|
|
||||||
|
|
||||||
|
public class InsightPumpFragment extends SubscriberFragment {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(InsightPumpFragment.class);
|
||||||
|
private static final Handler sLoopHandler = new Handler();
|
||||||
|
private static volatile boolean refresh = false;
|
||||||
|
private static volatile boolean pending = false;
|
||||||
|
StatusItemViewAdapter viewAdapter;
|
||||||
|
LinearLayout holder;
|
||||||
|
private final Runnable sRefreshLoop = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
pending = false;
|
||||||
|
updateGUI();
|
||||||
|
if (refresh) {
|
||||||
|
scheduleRefresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private synchronized void scheduleRefresh() {
|
||||||
|
if (!pending) {
|
||||||
|
pending = true;
|
||||||
|
sLoopHandler.postDelayed(sRefreshLoop, 30 * 1000L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
try {
|
||||||
|
final View view = inflater.inflate(R.layout.insightpump_fragment, container, false);
|
||||||
|
holder = (LinearLayout) view.findViewById(R.id.insightholder);
|
||||||
|
viewAdapter = new StatusItemViewAdapter(getActivity(), holder);
|
||||||
|
|
||||||
|
return view;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Crashlytics.logException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUserVisibleHint(boolean visible) {
|
||||||
|
super.setUserVisibleHint(visible);
|
||||||
|
if (visible) {
|
||||||
|
refresh = true;
|
||||||
|
pending = false;
|
||||||
|
updateGUI();
|
||||||
|
scheduleRefresh();
|
||||||
|
} else {
|
||||||
|
refresh = false;
|
||||||
|
//sLoopHandler.removeCallbacksAndMessages(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onStatusEvent(final EventInsightPumpUpdateGui ev) {
|
||||||
|
updateGUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateGUI() {
|
||||||
|
final Activity activity = getActivity();
|
||||||
|
if (activity != null && holder != null)
|
||||||
|
activity.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
final InsightPumpPlugin insightPumpPlugin = InsightPumpPlugin.getPlugin();
|
||||||
|
final List<StatusItem> l = insightPumpPlugin.getStatusItems(refresh);
|
||||||
|
|
||||||
|
holder.removeAllViews();
|
||||||
|
|
||||||
|
for (StatusItem row : l) {
|
||||||
|
viewAdapter.inflateStatus(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,940 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.BuildConfig;
|
||||||
|
import info.nightscout.androidaps.Config;
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.data.DetailedBolusInfo;
|
||||||
|
import info.nightscout.androidaps.data.Profile;
|
||||||
|
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||||
|
import info.nightscout.androidaps.db.ExtendedBolus;
|
||||||
|
import info.nightscout.androidaps.db.Source;
|
||||||
|
import info.nightscout.androidaps.db.TemporaryBasal;
|
||||||
|
import info.nightscout.androidaps.interfaces.PluginBase;
|
||||||
|
import info.nightscout.androidaps.interfaces.PumpDescription;
|
||||||
|
import info.nightscout.androidaps.interfaces.PumpInterface;
|
||||||
|
import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification;
|
||||||
|
import info.nightscout.androidaps.plugins.Overview.events.EventOverviewBolusProgress;
|
||||||
|
import info.nightscout.androidaps.plugins.Overview.notifications.Notification;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.connector.AbsoluteTBRTaskRunner;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.connector.CancelBolusTaskRunner;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.connector.Connector;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.events.EventInsightPumpCallback;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.events.EventInsightPumpUpdateGui;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.history.HistoryReceiver;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.history.LiveHistory;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.utils.StatusItem;
|
||||||
|
import info.nightscout.utils.DateUtil;
|
||||||
|
import info.nightscout.utils.NSUpload;
|
||||||
|
import info.nightscout.utils.SP;
|
||||||
|
import sugar.free.sightparser.applayer.descriptors.ActiveBolus;
|
||||||
|
import sugar.free.sightparser.applayer.descriptors.ActiveBolusType;
|
||||||
|
import sugar.free.sightparser.applayer.descriptors.PumpStatus;
|
||||||
|
import sugar.free.sightparser.applayer.messages.AppLayerMessage;
|
||||||
|
import sugar.free.sightparser.applayer.messages.remote_control.BolusMessage;
|
||||||
|
import sugar.free.sightparser.applayer.messages.remote_control.CancelTBRMessage;
|
||||||
|
import sugar.free.sightparser.applayer.messages.remote_control.ExtendedBolusMessage;
|
||||||
|
import sugar.free.sightparser.applayer.messages.remote_control.StandardBolusMessage;
|
||||||
|
import sugar.free.sightparser.handling.SingleMessageTaskRunner;
|
||||||
|
import sugar.free.sightparser.handling.TaskRunner;
|
||||||
|
import sugar.free.sightparser.handling.taskrunners.SetTBRTaskRunner;
|
||||||
|
import sugar.free.sightparser.handling.taskrunners.StatusTaskRunner;
|
||||||
|
|
||||||
|
import static info.nightscout.androidaps.plugins.PumpInsight.history.PumpIdCache.getRecordUniqueID;
|
||||||
|
import static info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers.roundDouble;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by jamorham on 23/01/2018.
|
||||||
|
*
|
||||||
|
* Connects to SightRemote app service using SightParser library
|
||||||
|
*
|
||||||
|
* SightRemote and SightParser created by Tebbe Ubben
|
||||||
|
*
|
||||||
|
* Original proof of concept SightProxy by jamorham
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class InsightPumpPlugin implements PluginBase, PumpInterface {
|
||||||
|
|
||||||
|
private static final long BUSY_WAIT_TIME = 20000;
|
||||||
|
static Integer batteryPercent = 0;
|
||||||
|
static Integer reservoirInUnits = 0;
|
||||||
|
static boolean initialized = false;
|
||||||
|
private static volatile boolean update_pending = false;
|
||||||
|
private static Logger log = LoggerFactory.getLogger(InsightPumpPlugin.class);
|
||||||
|
private static volatile InsightPumpPlugin plugin;
|
||||||
|
private final Handler handler = new Handler();
|
||||||
|
private final InsightPumpAsyncAdapter async = new InsightPumpAsyncAdapter();
|
||||||
|
private StatusTaskRunner.StatusResult statusResult;
|
||||||
|
private long statusResultTime = -1;
|
||||||
|
private Date lastDataTime = new Date(0);
|
||||||
|
private TaskRunner taskRunner;
|
||||||
|
private boolean fragmentEnabled = true;
|
||||||
|
private boolean fragmentVisible = true;
|
||||||
|
private boolean fauxTBRcancel = true;
|
||||||
|
private PumpDescription pumpDescription = new PumpDescription();
|
||||||
|
private double basalRate = 0;
|
||||||
|
private Connector connector;
|
||||||
|
private final TaskRunner.ResultCallback statusResultHandler = new TaskRunner.ResultCallback() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception e) {
|
||||||
|
log("Got error taskrunner: " + e);
|
||||||
|
android.util.Log.e("INSIGHTPUMP", "taskrunner stacktrace: ", e);
|
||||||
|
|
||||||
|
if (e instanceof sugar.free.sightparser.error.DisconnectedError) {
|
||||||
|
if (Helpers.ratelimit("insight-reconnect", 2)) {
|
||||||
|
Connector.connectToPump();
|
||||||
|
updateGui();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void onResult(Object result) {
|
||||||
|
log("GOT STATUS RESULT!!!");
|
||||||
|
statusResult = (StatusTaskRunner.StatusResult) result;
|
||||||
|
statusResultTime = Helpers.tsl();
|
||||||
|
processStatusResult();
|
||||||
|
updateGui();
|
||||||
|
connector.requestHistoryReSync();
|
||||||
|
connector.requestHistorySync();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private InsightPumpPlugin() {
|
||||||
|
log("InsightPumpPlugin");
|
||||||
|
pumpDescription.isBolusCapable = true;
|
||||||
|
pumpDescription.bolusStep = 0.05d; // specification says 0.05U up to 2U then 0.1U @ 2-5U 0.2U @ 10-20U 0.5U 10-20U (are these just UI restrictions?)
|
||||||
|
|
||||||
|
pumpDescription.isExtendedBolusCapable = true;
|
||||||
|
pumpDescription.extendedBolusStep = 0.05d; // specification probably same as above
|
||||||
|
pumpDescription.extendedBolusDurationStep = 15; // 15 minutes up to 24 hours
|
||||||
|
pumpDescription.extendedBolusMaxDuration = 24 * 60;
|
||||||
|
|
||||||
|
pumpDescription.isTempBasalCapable = true;
|
||||||
|
//pumpDescription.tempBasalStyle = PumpDescription.PERCENT | PumpDescription.ABSOLUTE;
|
||||||
|
pumpDescription.tempBasalStyle = PumpDescription.PERCENT;
|
||||||
|
|
||||||
|
pumpDescription.maxTempPercent = 250; // 0-250%
|
||||||
|
pumpDescription.tempPercentStep = 10;
|
||||||
|
|
||||||
|
pumpDescription.tempDurationStep = 15; // 15 minutes up to 24 hours
|
||||||
|
pumpDescription.tempMaxDuration = 24 * 60;
|
||||||
|
|
||||||
|
pumpDescription.isSetBasalProfileCapable = false; // leave this for now
|
||||||
|
pumpDescription.basalStep = 0.01d;
|
||||||
|
pumpDescription.basalMinimumRate = 0.02d;
|
||||||
|
|
||||||
|
pumpDescription.isRefillingCapable = true;
|
||||||
|
//pumpDescription.storesCarbInfo = false; // uncomment when PumpDescription updated to include this
|
||||||
|
|
||||||
|
this.connector = Connector.get();
|
||||||
|
this.connector.init();
|
||||||
|
|
||||||
|
log("back from init");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static InsightPumpPlugin getPlugin() {
|
||||||
|
if (plugin == null) {
|
||||||
|
createInstance();
|
||||||
|
}
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized void createInstance() {
|
||||||
|
if (plugin == null) {
|
||||||
|
log("creating instance");
|
||||||
|
plugin = new InsightPumpPlugin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// just log during debugging
|
||||||
|
private static void log(String msg) {
|
||||||
|
android.util.Log.e("INSIGHTPUMP", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateGui() {
|
||||||
|
update_pending = false;
|
||||||
|
MainApp.bus().post(new EventInsightPumpUpdateGui());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void pushCallbackEvent(EventInsightPumpCallback e) {
|
||||||
|
MainApp.bus().post(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFragmentClass() {
|
||||||
|
return InsightPumpFragment.class.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return MainApp.instance().getString(R.string.insightpump);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNameShort() {
|
||||||
|
String name = MainApp.instance().getString(R.string.insightpump_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 == PUMP && fragmentEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isVisibleInTabs(int type) {
|
||||||
|
return type == PUMP && fragmentVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 setFragmentEnabled(int type, boolean fragmentEnabled) {
|
||||||
|
if (type == PUMP) this.fragmentEnabled = fragmentEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFragmentVisible(int type, boolean fragmentVisible) {
|
||||||
|
if (type == PUMP) this.fragmentVisible = fragmentVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreferencesId() {
|
||||||
|
return R.xml.pref_insightpump;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getType() {
|
||||||
|
return PluginBase.PUMP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFakingTempsByExtendedBoluses() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSuspended() {
|
||||||
|
return !isPumpRunning();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBusy() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnected() {
|
||||||
|
return Connector.get().isPumpConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnecting() {
|
||||||
|
return Connector.get().isPumpConnecting();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connect(String reason) {
|
||||||
|
log("InsightPumpPlugin::connect()");
|
||||||
|
try {
|
||||||
|
if (!connector.isPumpConnected()) {
|
||||||
|
if (Helpers.ratelimit("insight-connect-timer", 40)) {
|
||||||
|
log("Actually requesting a connect");
|
||||||
|
connector.getServiceConnector().connect();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log("Already connected");
|
||||||
|
}
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
log("Could not sconnect - null pointer: " + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO review
|
||||||
|
if (!Config.NSCLIENT && !Config.G5UPLOADER)
|
||||||
|
NSUpload.uploadDeviceStatus();
|
||||||
|
lastDataTime = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect(String reason) {
|
||||||
|
log("InsightPumpPlugin::disconnect()");
|
||||||
|
try {
|
||||||
|
if (!SP.getBoolean("insight_always_connected", false)) {
|
||||||
|
log("Requesting disconnect");
|
||||||
|
connector.getServiceConnector().disconnect();
|
||||||
|
} else {
|
||||||
|
log("Not disconnecting due to preference");
|
||||||
|
}
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
log("Could not disconnect - null pointer: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stopConnecting() {
|
||||||
|
log("InsightPumpPlugin::stopConnecting()");
|
||||||
|
try {
|
||||||
|
if (isConnecting()) {
|
||||||
|
if (!SP.getBoolean("insight_always_connected", false)) {
|
||||||
|
log("Requesting disconnect");
|
||||||
|
connector.getServiceConnector().disconnect();
|
||||||
|
} else {
|
||||||
|
log("Not disconnecting due to preference");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log("Not currently trying to connect so not stopping connection");
|
||||||
|
}
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
log("Could not stop connecting - null pointer: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getPumpStatus() {
|
||||||
|
|
||||||
|
log("getPumpStatus");
|
||||||
|
lastDataTime = new Date();
|
||||||
|
if (Connector.get().isPumpConnected()) {
|
||||||
|
log("is connected.. requesting status");
|
||||||
|
handler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
taskRunner = new StatusTaskRunner(connector.getServiceConnector());
|
||||||
|
taskRunner.fetch(statusResultHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, 1000);
|
||||||
|
} else {
|
||||||
|
log("not connected.. not requesting status");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO implement
|
||||||
|
@Override
|
||||||
|
public PumpEnactResult setNewBasalProfile(Profile profile) {
|
||||||
|
lastDataTime = new Date();
|
||||||
|
// Do nothing here. we are using MainApp.getConfigBuilder().getActiveProfile().getProfile();
|
||||||
|
PumpEnactResult result = new PumpEnactResult();
|
||||||
|
result.enacted = false;
|
||||||
|
result.success = false;
|
||||||
|
Notification notification = new Notification(Notification.PROFILE_SET_OK, MainApp.sResources.getString(R.string.profile_set_ok), Notification.INFO, 60);
|
||||||
|
MainApp.bus().post(new EventNewNotification(notification));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isThisProfileSet(Profile profile) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date lastDataTime() {
|
||||||
|
return lastDataTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getBaseBasalRate() {
|
||||||
|
return basalRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBaseBasalRateString() {
|
||||||
|
final DecimalFormat df = new DecimalFormat("#.##");
|
||||||
|
return df.format(basalRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PumpEnactResult deliverTreatment(DetailedBolusInfo detailedBolusInfo) {
|
||||||
|
final PumpEnactResult result = new PumpEnactResult();
|
||||||
|
result.bolusDelivered = detailedBolusInfo.insulin;
|
||||||
|
result.carbsDelivered = detailedBolusInfo.carbs;
|
||||||
|
result.enacted = result.bolusDelivered > 0 || result.carbsDelivered > 0;
|
||||||
|
result.comment = MainApp.instance().getString(R.string.virtualpump_resultok);
|
||||||
|
|
||||||
|
result.percent = 100;
|
||||||
|
|
||||||
|
// is there an insulin component to the treatment?
|
||||||
|
if (detailedBolusInfo.insulin > 0) {
|
||||||
|
final UUID cmd = deliverBolus((float) detailedBolusInfo.insulin); // actually request delivery
|
||||||
|
if (cmd == null) {
|
||||||
|
return pumpEnactFailure();
|
||||||
|
}
|
||||||
|
final Cstatus cs = async.busyWaitForCommandResult(cmd, BUSY_WAIT_TIME);
|
||||||
|
result.success = cs.success();
|
||||||
|
if (cs.success()) {
|
||||||
|
detailedBolusInfo.pumpId = getRecordUniqueID(async.getResponseID(cmd));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.success = true; // always true with carb only treatments
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
log("Success!");
|
||||||
|
|
||||||
|
final EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance();
|
||||||
|
bolusingEvent.status = String.format(MainApp.sResources.getString(R.string.bolusdelivered), detailedBolusInfo.insulin);
|
||||||
|
bolusingEvent.percent = 100;
|
||||||
|
MainApp.bus().post(bolusingEvent);
|
||||||
|
MainApp.getConfigBuilder().addToHistoryTreatment(detailedBolusInfo);
|
||||||
|
} else {
|
||||||
|
log.debug("Failure to deliver treatment");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config.logPumpComm)
|
||||||
|
log.debug("Delivering treatment insulin: " + detailedBolusInfo.insulin + "U carbs: " + detailedBolusInfo.carbs + "g " + result);
|
||||||
|
|
||||||
|
updateGui();
|
||||||
|
connector.tryToGetPumpStatusAgain();
|
||||||
|
|
||||||
|
lastDataTime = new Date();
|
||||||
|
connector.requestHistorySync(30000);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stopBolusDelivering() {
|
||||||
|
final UUID cmd = aSyncTaskRunner(new CancelBolusTaskRunner(connector.getServiceConnector(), ActiveBolusType.STANDARD), "Cancel standard bolus");
|
||||||
|
|
||||||
|
if (cmd == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Cstatus cs = async.busyWaitForCommandResult(cmd, BUSY_WAIT_TIME);
|
||||||
|
log("Got command status: " + cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary Basals
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PumpEnactResult setTempBasalAbsolute(Double absoluteRate, Integer durationInMinutes, boolean enforceNew) {
|
||||||
|
absoluteRate = Helpers.roundDouble(absoluteRate, 3);
|
||||||
|
log("Set TBR absolute: " + absoluteRate);
|
||||||
|
|
||||||
|
final AbsoluteTBRTaskRunner task = new AbsoluteTBRTaskRunner(connector.getServiceConnector(), absoluteRate, durationInMinutes);
|
||||||
|
final UUID cmd = aSyncTaskRunner(task, "Set TBR abs: " + absoluteRate + " " + durationInMinutes + "m");
|
||||||
|
|
||||||
|
if (cmd == null) {
|
||||||
|
return pumpEnactFailure();
|
||||||
|
}
|
||||||
|
|
||||||
|
Cstatus cs = async.busyWaitForCommandResult(cmd, BUSY_WAIT_TIME);
|
||||||
|
log("Got command status: " + cs);
|
||||||
|
|
||||||
|
PumpEnactResult pumpEnactResult = new PumpEnactResult().enacted(true).isPercent(false).duration(durationInMinutes);
|
||||||
|
pumpEnactResult.absolute = absoluteRate; // TODO get converted value?
|
||||||
|
pumpEnactResult.success = cs.success();
|
||||||
|
pumpEnactResult.isTempCancel = false; // do we test this here?
|
||||||
|
pumpEnactResult.comment = async.getCommandComment(cmd);
|
||||||
|
|
||||||
|
if (pumpEnactResult.success) {
|
||||||
|
// create log entry
|
||||||
|
final TemporaryBasal tempBasal = new TemporaryBasal();
|
||||||
|
tempBasal.date = System.currentTimeMillis();
|
||||||
|
tempBasal.isAbsolute = true;
|
||||||
|
tempBasal.absoluteRate = task.getCalculatedAbsolute(); // is this the correct figure to use?
|
||||||
|
tempBasal.durationInMinutes = durationInMinutes;
|
||||||
|
tempBasal.source = Source.USER;
|
||||||
|
MainApp.getConfigBuilder().addToHistoryTempBasal(tempBasal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config.logPumpComm)
|
||||||
|
log.debug("Setting temp basal absolute: " + pumpEnactResult.success);
|
||||||
|
|
||||||
|
lastDataTime = new Date();
|
||||||
|
|
||||||
|
updateGui();
|
||||||
|
|
||||||
|
connector.requestHistorySync(5000);
|
||||||
|
connector.tryToGetPumpStatusAgain();
|
||||||
|
|
||||||
|
return pumpEnactResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PumpEnactResult setTempBasalPercent(Integer percent, Integer durationInMinutes, boolean enforceNew) {
|
||||||
|
log("Set TBR %");
|
||||||
|
final UUID cmd = aSyncTaskRunner(new SetTBRTaskRunner(connector.getServiceConnector(), percent, durationInMinutes), "Set TBR " + percent + "%" + " " + durationInMinutes + "m");
|
||||||
|
|
||||||
|
if (cmd == null) {
|
||||||
|
return pumpEnactFailure();
|
||||||
|
}
|
||||||
|
|
||||||
|
Cstatus cs = async.busyWaitForCommandResult(cmd, BUSY_WAIT_TIME);
|
||||||
|
log("Got command status: " + cs);
|
||||||
|
|
||||||
|
PumpEnactResult pumpEnactResult = new PumpEnactResult().enacted(true).isPercent(true).duration(durationInMinutes);
|
||||||
|
pumpEnactResult.percent = percent;
|
||||||
|
pumpEnactResult.success = cs.success();
|
||||||
|
pumpEnactResult.isTempCancel = percent == 100; // 100% temp basal is a cancellation
|
||||||
|
pumpEnactResult.comment = async.getCommandComment(cmd);
|
||||||
|
|
||||||
|
if (pumpEnactResult.success) {
|
||||||
|
// create log entry
|
||||||
|
final TemporaryBasal tempBasal = new TemporaryBasal();
|
||||||
|
tempBasal.date = System.currentTimeMillis();
|
||||||
|
tempBasal.isAbsolute = false;
|
||||||
|
tempBasal.percentRate = percent;
|
||||||
|
tempBasal.durationInMinutes = durationInMinutes;
|
||||||
|
tempBasal.source = Source.USER; // TODO check this is correct
|
||||||
|
MainApp.getConfigBuilder().addToHistoryTempBasal(tempBasal);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGui();
|
||||||
|
|
||||||
|
if (Config.logPumpComm)
|
||||||
|
log.debug("Set temp basal " + percent + "% for " + durationInMinutes + "m");
|
||||||
|
|
||||||
|
connector.requestHistorySync(5000);
|
||||||
|
connector.tryToGetPumpStatusAgain();
|
||||||
|
|
||||||
|
return pumpEnactResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PumpEnactResult cancelTempBasal(boolean enforceNew) {
|
||||||
|
log("Cancel TBR");
|
||||||
|
|
||||||
|
|
||||||
|
fauxTBRcancel = !SP.getBoolean("insight_real_tbr_cancel", false);
|
||||||
|
|
||||||
|
final UUID cmd;
|
||||||
|
|
||||||
|
if (fauxTBRcancel) {
|
||||||
|
final int faux_percent = 90;
|
||||||
|
final int faux_duration = 15;
|
||||||
|
cmd = aSyncTaskRunner(new SetTBRTaskRunner(connector.getServiceConnector(), faux_percent, 15), "Faux Cancel TBR - setting " + faux_percent + "%" + " " + faux_duration + "m");
|
||||||
|
} else {
|
||||||
|
cmd = aSyncSingleCommand(new CancelTBRMessage(), "Cancel Temp Basal");
|
||||||
|
}
|
||||||
|
if (cmd == null) {
|
||||||
|
return pumpEnactFailure();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO isn't conditional on one apparently being in progress only the history change
|
||||||
|
boolean enacted = false;
|
||||||
|
final Cstatus cs = async.busyWaitForCommandResult(cmd, BUSY_WAIT_TIME);
|
||||||
|
|
||||||
|
if (MainApp.getConfigBuilder().isTempBasalInProgress()) {
|
||||||
|
enacted = true;
|
||||||
|
TemporaryBasal tempStop = new TemporaryBasal(System.currentTimeMillis());
|
||||||
|
tempStop.source = Source.USER;
|
||||||
|
MainApp.getConfigBuilder().addToHistoryTempBasal(tempStop);
|
||||||
|
}
|
||||||
|
lastDataTime = new Date();
|
||||||
|
updateGui();
|
||||||
|
if (Config.logPumpComm)
|
||||||
|
log.debug("Canceling temp basal: "); // TODO get more info
|
||||||
|
|
||||||
|
connector.requestHistorySync(5000);
|
||||||
|
connector.tryToGetPumpStatusAgain();
|
||||||
|
|
||||||
|
return new PumpEnactResult().success(cs.success()).enacted(true).isTempCancel(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Extended Boluses
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PumpEnactResult setExtendedBolus(Double insulin, Integer durationInMinutes) {
|
||||||
|
log("Set Extended bolus " + insulin + " " + durationInMinutes);
|
||||||
|
ExtendedBolusMessage extendedBolusMessage = new ExtendedBolusMessage();
|
||||||
|
extendedBolusMessage.setAmount((float) ((double) insulin));
|
||||||
|
extendedBolusMessage.setDuration((short) ((int) durationInMinutes));
|
||||||
|
final UUID cmd = aSyncSingleCommand(extendedBolusMessage, "Extended bolus U" + insulin + " mins:" + durationInMinutes);
|
||||||
|
if (cmd == null) {
|
||||||
|
return pumpEnactFailure();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Cstatus cs = async.busyWaitForCommandResult(cmd, BUSY_WAIT_TIME);
|
||||||
|
log("Got command status: " + cs);
|
||||||
|
|
||||||
|
PumpEnactResult pumpEnactResult = new PumpEnactResult().enacted(true).bolusDelivered(insulin).duration(durationInMinutes);
|
||||||
|
pumpEnactResult.success = cs.success();
|
||||||
|
pumpEnactResult.comment = async.getCommandComment(cmd);
|
||||||
|
|
||||||
|
if (pumpEnactResult.success) {
|
||||||
|
// create log entry
|
||||||
|
final ExtendedBolus extendedBolus = new ExtendedBolus();
|
||||||
|
extendedBolus.date = System.currentTimeMillis();
|
||||||
|
extendedBolus.insulin = insulin;
|
||||||
|
extendedBolus.durationInMinutes = durationInMinutes;
|
||||||
|
extendedBolus.source = Source.USER;
|
||||||
|
extendedBolus.pumpId = getRecordUniqueID(async.getResponseID(cmd));
|
||||||
|
MainApp.getConfigBuilder().addToHistoryExtendedBolus(extendedBolus);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config.logPumpComm)
|
||||||
|
log.debug("Setting extended bolus: " + insulin + " mins:" + durationInMinutes + " " + pumpEnactResult.comment);
|
||||||
|
|
||||||
|
updateGui();
|
||||||
|
|
||||||
|
connector.requestHistorySync(30000);
|
||||||
|
connector.tryToGetPumpStatusAgain();
|
||||||
|
|
||||||
|
return pumpEnactResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PumpEnactResult cancelExtendedBolus() {
|
||||||
|
|
||||||
|
log("Cancel Extended bolus");
|
||||||
|
|
||||||
|
// TODO note always sends cancel to pump but only changes history if present
|
||||||
|
|
||||||
|
final UUID cmd = aSyncTaskRunner(new CancelBolusTaskRunner(connector.getServiceConnector(), ActiveBolusType.EXTENDED), "Cancel extended bolus");
|
||||||
|
|
||||||
|
if (cmd == null) {
|
||||||
|
return pumpEnactFailure();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Cstatus cs = async.busyWaitForCommandResult(cmd, BUSY_WAIT_TIME);
|
||||||
|
|
||||||
|
if (MainApp.getConfigBuilder().isInHistoryExtendedBoluslInProgress()) {
|
||||||
|
ExtendedBolus exStop = new ExtendedBolus(System.currentTimeMillis());
|
||||||
|
exStop.source = Source.USER;
|
||||||
|
MainApp.getConfigBuilder().addToHistoryExtendedBolus(exStop);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config.logPumpComm)
|
||||||
|
log.debug("Cancel extended bolus:");
|
||||||
|
|
||||||
|
updateGui();
|
||||||
|
|
||||||
|
connector.requestHistorySync(5000);
|
||||||
|
connector.tryToGetPumpStatusAgain();
|
||||||
|
|
||||||
|
return new PumpEnactResult().success(cs.success()).enacted(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private synchronized UUID deliverBolus(float bolusValue) {
|
||||||
|
log("DeliverBolus: " + bolusValue);
|
||||||
|
|
||||||
|
if (bolusValue == 0) return null;
|
||||||
|
if (bolusValue < 0) return null;
|
||||||
|
|
||||||
|
// TODO check limits here or they already occur via a previous constraint interface?
|
||||||
|
|
||||||
|
final StandardBolusMessage message = new StandardBolusMessage();
|
||||||
|
message.setAmount(bolusValue);
|
||||||
|
|
||||||
|
return aSyncSingleCommand(message, "Deliver Bolus " + bolusValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject getJSONStatus() {
|
||||||
|
|
||||||
|
if (Helpers.msSince(connector.getLastContactTime()) > (60 * 60 * 1000)) {
|
||||||
|
log("getJSONStatus not returning as data likely stale");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final JSONObject pump = new JSONObject();
|
||||||
|
final JSONObject battery = new JSONObject();
|
||||||
|
final JSONObject status = new JSONObject();
|
||||||
|
final JSONObject extended = new JSONObject();
|
||||||
|
try {
|
||||||
|
battery.put("percent", batteryPercent);
|
||||||
|
status.put("status", isSuspended() ? "suspended" : "normal");
|
||||||
|
status.put("timestamp", DateUtil.toISOString(connector.getLastContactTime()));
|
||||||
|
extended.put("Version", BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION);
|
||||||
|
try {
|
||||||
|
extended.put("ActiveProfile", MainApp.getConfigBuilder().getProfileName());
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
TemporaryBasal tb = MainApp.getConfigBuilder().getTempBasalFromHistory(System.currentTimeMillis());
|
||||||
|
if (tb != null) {
|
||||||
|
extended.put("TempBasalAbsoluteRate", tb.tempBasalConvertedToAbsolute(System.currentTimeMillis()));
|
||||||
|
extended.put("TempBasalStart", DateUtil.dateAndTimeString(tb.date));
|
||||||
|
extended.put("TempBasalRemaining", tb.getPlannedRemainingMinutes());
|
||||||
|
}
|
||||||
|
ExtendedBolus eb = MainApp.getConfigBuilder().getExtendedBolusFromHistory(System.currentTimeMillis());
|
||||||
|
if (eb != null) {
|
||||||
|
extended.put("ExtendedBolusAbsoluteRate", eb.absoluteRate());
|
||||||
|
extended.put("ExtendedBolusStart", DateUtil.dateAndTimeString(eb.date));
|
||||||
|
extended.put("ExtendedBolusRemaining", eb.getPlannedRemainingMinutes());
|
||||||
|
}
|
||||||
|
status.put("timestamp", DateUtil.toISOString(new Date()));
|
||||||
|
|
||||||
|
pump.put("battery", battery);
|
||||||
|
pump.put("status", status);
|
||||||
|
pump.put("extended", extended);
|
||||||
|
pump.put("reservoir", reservoirInUnits);
|
||||||
|
pump.put("clock", DateUtil.toISOString(new Date()));
|
||||||
|
} catch (JSONException e) {
|
||||||
|
log.error("Unhandled exception", e);
|
||||||
|
}
|
||||||
|
return pump;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String deviceID() {
|
||||||
|
return "InsightPump";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PumpDescription getPumpDescription() {
|
||||||
|
return pumpDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String shortStatus(boolean veryShort) {
|
||||||
|
String msg = gs(R.string.insightpump_shortname) + " Batt: " + batteryPercent + " Reserv: " + reservoirInUnits + " Basal: " + basalRate;
|
||||||
|
if (LiveHistory.getStatus().length() > 0) {
|
||||||
|
msg += LiveHistory.getStatus();
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processStatusResult() {
|
||||||
|
if (statusResult != null) {
|
||||||
|
batteryPercent = statusResult.getBatteryAmountMessage().getBatteryAmount();
|
||||||
|
reservoirInUnits = (int) statusResult.getCartridgeAmountMessage().getCartridgeAmount();
|
||||||
|
basalRate = roundDouble(statusResult.getCurrentBasalMessage().getCurrentBasalAmount(), 2);
|
||||||
|
initialized = true; // basic communication test
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String gs(int id) {
|
||||||
|
return MainApp.instance().getString(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPumpRunning() {
|
||||||
|
if (statusResult == null) return true; // assume running if we have no information
|
||||||
|
return statusResult.getPumpStatusMessage().getPumpStatus() == PumpStatus.STARTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<StatusItem> getStatusItems(boolean refresh) {
|
||||||
|
final List<StatusItem> l = new ArrayList<>();
|
||||||
|
|
||||||
|
// Todo last contact time
|
||||||
|
|
||||||
|
l.add(new StatusItem(gs(R.string.status_no_colon), connector.getLastStatusMessage()));
|
||||||
|
l.add(new StatusItem(gs(R.string.changed), connector.getNiceLastStatusTime()));
|
||||||
|
|
||||||
|
boolean pumpRunning;
|
||||||
|
// also check time since received
|
||||||
|
if (statusResult != null) {
|
||||||
|
|
||||||
|
pumpRunning = isPumpRunning();
|
||||||
|
if (pumpRunning) {
|
||||||
|
l.add(new StatusItem(gs(R.string.pump_basebasalrate_label), getBaseBasalRateString() + "U"));
|
||||||
|
} else {
|
||||||
|
l.add(new StatusItem(gs(R.string.combo_warning), gs(R.string.pump_stopped_uppercase), StatusItem.Highlight.CRITICAL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final long offset_ms = Helpers.msSince(statusResultTime);
|
||||||
|
final long offset_minutes = offset_ms / 60000;
|
||||||
|
|
||||||
|
if (statusResult != null) {
|
||||||
|
l.add(new StatusItem(gs(R.string.status_updated), Helpers.niceTimeScalar(Helpers.msSince(statusResultTime)) + " " + gs(R.string.ago)));
|
||||||
|
l.add(new StatusItem(gs(R.string.pump_battery_label), batteryPercent + "%", batteryPercent < 100 ?
|
||||||
|
(batteryPercent < 90 ?
|
||||||
|
(batteryPercent < 70 ?
|
||||||
|
(StatusItem.Highlight.BAD) : StatusItem.Highlight.NOTICE) : StatusItem.Highlight.NORMAL) : StatusItem.Highlight.GOOD));
|
||||||
|
l.add(new StatusItem(gs(R.string.pump_reservoir_label), reservoirInUnits + "U"));
|
||||||
|
|
||||||
|
if (statusResult.getCurrentTBRMessage().getPercentage() != 100) {
|
||||||
|
l.add(new StatusItem(gs(R.string.insight_active_tbr), statusResult.getCurrentTBRMessage().getPercentage() + "% " + gs(R.string.with) + " "
|
||||||
|
+ Helpers.qs(statusResult.getCurrentTBRMessage().getLeftoverTime() - offset_minutes, 0)
|
||||||
|
+ " " + gs(R.string.insight_min_left), StatusItem.Highlight.NOTICE));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MainApp.getConfigBuilder().isTempBasalInProgress()) {
|
||||||
|
try {
|
||||||
|
l.add(new StatusItem(gs(R.string.pump_tempbasal_label), MainApp.getConfigBuilder().getTempBasalFromHistory(System.currentTimeMillis()).toStringFull()));
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusResult != null) {
|
||||||
|
statusActiveBolus(statusResult.getActiveBolusesMessage().getBolus1(), offset_minutes, l);
|
||||||
|
statusActiveBolus(statusResult.getActiveBolusesMessage().getBolus2(), offset_minutes, l);
|
||||||
|
statusActiveBolus(statusResult.getActiveBolusesMessage().getBolus3(), offset_minutes, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MainApp.getConfigBuilder().isInHistoryExtendedBoluslInProgress()) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
l.add(new StatusItem(gs(R.string.virtualpump_extendedbolus_label), MainApp.getConfigBuilder().getExtendedBolusFromHistory(System.currentTimeMillis()).toString()));
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l.add(new StatusItem(gs(R.string.log_book), HistoryReceiver.getStatusString()));
|
||||||
|
|
||||||
|
if (LiveHistory.getStatus().length() > 0) {
|
||||||
|
l.add(new StatusItem(gs(R.string.insight_last_completed_action), LiveHistory.getStatus()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Helpers.ratelimit("insight-status-ui-refresh", 10)) {
|
||||||
|
connector.tryToGetPumpStatusAgain();
|
||||||
|
}
|
||||||
|
connector.requestHistorySync();
|
||||||
|
if (refresh) scheduleGUIUpdate();
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void scheduleGUIUpdate() {
|
||||||
|
if (!update_pending && connector.uiFresh()) {
|
||||||
|
update_pending = true;
|
||||||
|
Helpers.runOnUiThreadDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
updateGui();
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void statusActiveBolus(ActiveBolus activeBolus, long offset_mins, List<StatusItem> l) {
|
||||||
|
if (activeBolus == null) return;
|
||||||
|
switch (activeBolus.getBolusType()) {
|
||||||
|
|
||||||
|
case STANDARD:
|
||||||
|
l.add(new StatusItem(activeBolus.getBolusType() + " " + gs(R.string.bolus), activeBolus.getInitialAmount() + "U", StatusItem.Highlight.NOTICE));
|
||||||
|
break;
|
||||||
|
case EXTENDED:
|
||||||
|
l.add(new StatusItem(activeBolus.getBolusType() + " " + gs(R.string.bolus), activeBolus.getInitialAmount() + "U " + gs(R.string.insight_total_with) + " "
|
||||||
|
+ activeBolus.getLeftoverAmount() + "U " + gs(R.string.insight_remaining_over) + " " + (activeBolus.getDuration() - offset_mins) + " " + gs(R.string.insight_min), StatusItem.Highlight.NOTICE));
|
||||||
|
break;
|
||||||
|
case MULTIWAVE:
|
||||||
|
l.add(new StatusItem(activeBolus.getBolusType() + " " + gs(R.string.bolus), activeBolus.getInitialAmount() + "U " + gs(R.string.insight_upfront_with) + " "
|
||||||
|
+ activeBolus.getLeftoverAmount() + "U " + gs(R.string.insight_remaining_over) + " " + (activeBolus.getDuration() - offset_mins) + " " + gs(R.string.insight_min), StatusItem.Highlight.NOTICE));
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log("ERROR: unknown bolus type! " + activeBolus.getBolusType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility
|
||||||
|
|
||||||
|
private synchronized UUID aSyncSingleCommand(final AppLayerMessage msg, final String name) {
|
||||||
|
// if (!isConnected()) return false;
|
||||||
|
//if (isBusy()) return false;
|
||||||
|
log("asyncSinglecommand called: " + name);
|
||||||
|
final EventInsightPumpCallback event = new EventInsightPumpCallback();
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
log("asyncSingleCommand thread");
|
||||||
|
final SingleMessageTaskRunner singleMessageTaskRunner = new SingleMessageTaskRunner(connector.getServiceConnector(), msg);
|
||||||
|
try {
|
||||||
|
singleMessageTaskRunner.fetch(new TaskRunner.ResultCallback() {
|
||||||
|
@Override
|
||||||
|
public void onResult(Object o) {
|
||||||
|
log(name + " success");
|
||||||
|
if (o instanceof BolusMessage) {
|
||||||
|
event.response_id = ((BolusMessage)o).getBolusId();
|
||||||
|
}
|
||||||
|
event.success = true;
|
||||||
|
pushCallbackEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception e) {
|
||||||
|
log(name + " error");
|
||||||
|
event.message = e.getMessage();
|
||||||
|
pushCallbackEvent(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log("EXCEPTION" + e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
return event.request_uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized UUID aSyncTaskRunner(final TaskRunner task, final String name) {
|
||||||
|
// if (!isConnected()) return false;
|
||||||
|
//if (isBusy()) return false;
|
||||||
|
log("asyncTaskRunner called: " + name);
|
||||||
|
final EventInsightPumpCallback event = new EventInsightPumpCallback();
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
log("asyncTaskRunner thread");
|
||||||
|
try {
|
||||||
|
task.fetch(new TaskRunner.ResultCallback() {
|
||||||
|
@Override
|
||||||
|
public void onResult(Object o) {
|
||||||
|
log(name + " success");
|
||||||
|
event.success = true;
|
||||||
|
pushCallbackEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception e) {
|
||||||
|
log(name + " error");
|
||||||
|
event.message = e.getMessage();
|
||||||
|
pushCallbackEvent(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log("EXCEPTION" + e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
return event.request_uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private PumpEnactResult pumpEnactFailure() {
|
||||||
|
return new PumpEnactResult().success(false).enacted(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight.connector;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers;
|
||||||
|
import sugar.free.sightparser.applayer.messages.AppLayerMessage;
|
||||||
|
import sugar.free.sightparser.applayer.messages.remote_control.ChangeTBRMessage;
|
||||||
|
import sugar.free.sightparser.applayer.messages.remote_control.SetTBRMessage;
|
||||||
|
import sugar.free.sightparser.applayer.messages.status.CurrentBasalMessage;
|
||||||
|
import sugar.free.sightparser.applayer.messages.status.CurrentTBRMessage;
|
||||||
|
import sugar.free.sightparser.handling.SightServiceConnector;
|
||||||
|
import sugar.free.sightparser.handling.TaskRunner;
|
||||||
|
|
||||||
|
// by Tebbe Ubben
|
||||||
|
|
||||||
|
public class AbsoluteTBRTaskRunner extends TaskRunner {
|
||||||
|
|
||||||
|
private double absolute;
|
||||||
|
private int amount;
|
||||||
|
private int duration;
|
||||||
|
private int calculated_percentage;
|
||||||
|
private double calculated_absolute;
|
||||||
|
|
||||||
|
public AbsoluteTBRTaskRunner(SightServiceConnector serviceConnector, double absolute, int duration) {
|
||||||
|
super(serviceConnector);
|
||||||
|
if (absolute < 0) absolute = 0;
|
||||||
|
this.absolute = absolute;
|
||||||
|
this.duration = duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCalculatedPercentage() {
|
||||||
|
return calculated_percentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCalculatedAbsolute() {
|
||||||
|
return calculated_absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AppLayerMessage run(AppLayerMessage message) throws Exception {
|
||||||
|
if (message == null) return new CurrentBasalMessage();
|
||||||
|
else if (message instanceof CurrentBasalMessage) {
|
||||||
|
float currentBasal = ((CurrentBasalMessage) message).getCurrentBasalAmount();
|
||||||
|
amount = (int) (100d / currentBasal * absolute);
|
||||||
|
amount = ((int) amount / 10) * 10;
|
||||||
|
if (amount > 250) amount = 250;
|
||||||
|
calculated_percentage = amount;
|
||||||
|
calculated_absolute = Helpers.roundDouble(calculated_percentage * (double) currentBasal / 100d, 3);
|
||||||
|
Connector.log("Asked: " + absolute + " current: " + currentBasal + " calculated as: " + amount + "%" + " = " + calculated_absolute);
|
||||||
|
return new CurrentTBRMessage();
|
||||||
|
} else if (message instanceof CurrentTBRMessage) {
|
||||||
|
SetTBRMessage setTBRMessage;
|
||||||
|
if (((CurrentTBRMessage) message).getPercentage() == 100)
|
||||||
|
setTBRMessage = new SetTBRMessage();
|
||||||
|
else setTBRMessage = new ChangeTBRMessage();
|
||||||
|
setTBRMessage.setAmount((short) amount);
|
||||||
|
setTBRMessage.setDuration((short) duration);
|
||||||
|
return setTBRMessage;
|
||||||
|
} else if (message instanceof SetTBRMessage) finish(amount);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight.connector;
|
||||||
|
|
||||||
|
import sugar.free.sightparser.applayer.messages.AppLayerMessage;
|
||||||
|
import sugar.free.sightparser.applayer.descriptors.ActiveBolusType;
|
||||||
|
import sugar.free.sightparser.applayer.messages.remote_control.CancelBolusMessage;
|
||||||
|
import sugar.free.sightparser.applayer.messages.status.ActiveBolusesMessage;
|
||||||
|
import sugar.free.sightparser.handling.SightServiceConnector;
|
||||||
|
import sugar.free.sightparser.handling.TaskRunner;
|
||||||
|
|
||||||
|
// by Tebbe Ubben
|
||||||
|
|
||||||
|
public class CancelBolusTaskRunner extends TaskRunner {
|
||||||
|
|
||||||
|
private ActiveBolusType bolusType;
|
||||||
|
|
||||||
|
public CancelBolusTaskRunner(SightServiceConnector serviceConnector, ActiveBolusType bolusType) {
|
||||||
|
super(serviceConnector);
|
||||||
|
this.bolusType = bolusType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AppLayerMessage run(AppLayerMessage message) throws Exception {
|
||||||
|
if (message == null) return new ActiveBolusesMessage();
|
||||||
|
else if (message instanceof ActiveBolusesMessage) {
|
||||||
|
ActiveBolusesMessage bolusesMessage = (ActiveBolusesMessage) message;
|
||||||
|
CancelBolusMessage cancelBolusMessage = new CancelBolusMessage();
|
||||||
|
if (bolusesMessage.getBolus1().getBolusType() == bolusType)
|
||||||
|
cancelBolusMessage.setBolusId(bolusesMessage.getBolus1().getBolusID());
|
||||||
|
else if (bolusesMessage.getBolus2().getBolusType() == bolusType)
|
||||||
|
cancelBolusMessage.setBolusId(bolusesMessage.getBolus2().getBolusID());
|
||||||
|
else if (bolusesMessage.getBolus3().getBolusType() == bolusType)
|
||||||
|
cancelBolusMessage.setBolusId(bolusesMessage.getBolus3().getBolusID());
|
||||||
|
else finish(null);
|
||||||
|
return cancelBolusMessage;
|
||||||
|
} else if (message instanceof CancelBolusMessage) finish(null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,357 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight.connector;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.events.EventInsightPumpUpdateGui;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.history.HistoryReceiver;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.history.LiveHistory;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers;
|
||||||
|
import sugar.free.sightparser.handling.ServiceConnectionCallback;
|
||||||
|
import sugar.free.sightparser.handling.SightServiceConnector;
|
||||||
|
import sugar.free.sightparser.handling.StatusCallback;
|
||||||
|
import sugar.free.sightparser.pipeline.Status;
|
||||||
|
|
||||||
|
import static sugar.free.sightparser.handling.HistoryBroadcast.ACTION_START_RESYNC;
|
||||||
|
import static sugar.free.sightparser.handling.HistoryBroadcast.ACTION_START_SYNC;
|
||||||
|
import static sugar.free.sightparser.handling.SightService.COMPATIBILITY_VERSION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by jamorham on 23/01/2018.
|
||||||
|
*
|
||||||
|
* Connects to SightRemote app service using SightParser library
|
||||||
|
*
|
||||||
|
* SightRemote and SightParser created by Tebbe Ubben
|
||||||
|
*
|
||||||
|
* Original proof of concept SightProxy by jamorham
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Connector {
|
||||||
|
|
||||||
|
private static final String TAG = "InsightConnector";
|
||||||
|
private static final String COMPANION_APP_PACKAGE = "sugar.free.sightremote";
|
||||||
|
private final static long FRESH_MS = 70000;
|
||||||
|
private static volatile Connector instance;
|
||||||
|
private static volatile HistoryReceiver historyReceiver;
|
||||||
|
private volatile SightServiceConnector serviceConnector;
|
||||||
|
private volatile Status lastStatus = null;
|
||||||
|
private String compatabilityMessage = null;
|
||||||
|
private volatile long lastStatusTime = -1;
|
||||||
|
private volatile long lastContactTime = -1;
|
||||||
|
private boolean companionAppInstalled = false;
|
||||||
|
private int serviceReconnects = 0;
|
||||||
|
private StatusCallback statusCallback = new StatusCallback() {
|
||||||
|
@Override
|
||||||
|
public synchronized void onStatusChange(Status status) {
|
||||||
|
|
||||||
|
log("Status change: " + status);
|
||||||
|
lastStatus = status;
|
||||||
|
lastStatusTime = Helpers.tsl();
|
||||||
|
if (status == Status.CONNECTED) {
|
||||||
|
lastContactTime = lastStatusTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
MainApp.bus().post(new EventInsightPumpUpdateGui());
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
private ServiceConnectionCallback connectionCallback = new ServiceConnectionCallback() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void onServiceConnected() {
|
||||||
|
log("On service connected");
|
||||||
|
try {
|
||||||
|
final String remoteVersion = serviceConnector.getRemoteVersion();
|
||||||
|
if (remoteVersion.equals(COMPATIBILITY_VERSION)) {
|
||||||
|
serviceConnector.connect();
|
||||||
|
} else {
|
||||||
|
log("PROTOCOL VERSION MISMATCH! local: " + COMPATIBILITY_VERSION + " remote: " + remoteVersion);
|
||||||
|
statusCallback.onStatusChange(Status.INCOMPATIBLE);
|
||||||
|
compatabilityMessage = gs(R.string.insight_incompatible_compantion_app_we_need_version) + " " + getLocalVersion();
|
||||||
|
serviceConnector.disconnectFromService();
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
log("ERROR: null pointer when trying to connect to pump");
|
||||||
|
}
|
||||||
|
statusCallback.onStatusChange(safeGetStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void onServiceDisconnected() {
|
||||||
|
log("Disconnected from service");
|
||||||
|
if (Helpers.ratelimit("insight-automatic-reconnect", 30)) {
|
||||||
|
log("Scheduling automatic service reconnection");
|
||||||
|
Helpers.runOnUiThreadDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
}, 20000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private Connector() {
|
||||||
|
initializeHistoryReceiver();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Connector get() {
|
||||||
|
if (instance == null) {
|
||||||
|
init_instance();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized static void init_instance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new Connector();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isCompanionAppInstalled() {
|
||||||
|
return Helpers.checkPackageExists(MainApp.instance(), TAG, COMPANION_APP_PACKAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void connectToPump() {
|
||||||
|
log("Attempting to connect to pump");
|
||||||
|
get().getServiceConnector().connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log(String msg) {
|
||||||
|
android.util.Log.e("INSIGHTPUMP", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getLocalVersion() {
|
||||||
|
return COMPATIBILITY_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String statusToString(Status status) {
|
||||||
|
switch (status) {
|
||||||
|
|
||||||
|
case EXCHANGING_KEYS:
|
||||||
|
return gs(R.string.connecting).toUpperCase();
|
||||||
|
case WAITING_FOR_CODE_CONFIRMATION:
|
||||||
|
return gs(R.string.insight_waiting_for_code).toUpperCase();
|
||||||
|
case CODE_REJECTED:
|
||||||
|
return gs(R.string.insight_code_rejected).toUpperCase();
|
||||||
|
case APP_BINDING:
|
||||||
|
return gs(R.string.insight_app_binding).toUpperCase();
|
||||||
|
case CONNECTING:
|
||||||
|
return gs(R.string.connecting).toUpperCase();
|
||||||
|
case CONNECTED:
|
||||||
|
return gs(R.string.connected).toUpperCase();
|
||||||
|
case DISCONNECTED:
|
||||||
|
return gs(R.string.disconnected).toUpperCase();
|
||||||
|
case NOT_AUTHORIZED:
|
||||||
|
return gs(R.string.insight_not_authorized).toUpperCase();
|
||||||
|
case INCOMPATIBLE:
|
||||||
|
return gs(R.string.insight_incompatible).toUpperCase();
|
||||||
|
|
||||||
|
default:
|
||||||
|
return status.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String gs(int id) {
|
||||||
|
return MainApp.instance().getString(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("AccessStaticViaInstance")
|
||||||
|
private synchronized void initializeHistoryReceiver() {
|
||||||
|
if (historyReceiver == null) {
|
||||||
|
historyReceiver = new HistoryReceiver();
|
||||||
|
}
|
||||||
|
historyReceiver.registerHistoryReceiver();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void init() {
|
||||||
|
log("Connector::init()");
|
||||||
|
if (serviceConnector == null) {
|
||||||
|
companionAppInstalled = isCompanionAppInstalled();
|
||||||
|
if (companionAppInstalled) {
|
||||||
|
serviceConnector = new SightServiceConnector(MainApp.instance());
|
||||||
|
serviceConnector.removeStatusCallback(statusCallback);
|
||||||
|
serviceConnector.addStatusCallback(statusCallback);
|
||||||
|
serviceConnector.setConnectionCallback(connectionCallback);
|
||||||
|
serviceConnector.connectToService();
|
||||||
|
log("Trying to connect");
|
||||||
|
} else {
|
||||||
|
log("Not trying init due to missing companion app");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!serviceConnector.isConnectedToService()) {
|
||||||
|
if (serviceReconnects > 0) {
|
||||||
|
serviceConnector = null;
|
||||||
|
init();
|
||||||
|
} else {
|
||||||
|
log("Trying to reconnect to service (" + serviceReconnects + ")");
|
||||||
|
serviceConnector.connectToService();
|
||||||
|
serviceReconnects++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
serviceReconnects = 0; // everything ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SightServiceConnector getServiceConnector() {
|
||||||
|
init();
|
||||||
|
return serviceConnector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurrent() {
|
||||||
|
init();
|
||||||
|
return safeGetStatus().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Status safeGetStatus() {
|
||||||
|
try {
|
||||||
|
if (isConnected()) return serviceConnector.getStatus();
|
||||||
|
return Status.DISCONNECTED;
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Status.INCOMPATIBLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Status getLastStatus() {
|
||||||
|
return lastStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnected() {
|
||||||
|
return serviceConnector != null && serviceConnector.isConnectedToService();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPumpConnected() {
|
||||||
|
return isConnected() && getLastStatus() == Status.CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPumpConnecting() {
|
||||||
|
return isConnected() && getLastStatus() == Status.CONNECTING;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLastContactTime() {
|
||||||
|
return lastContactTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastStatusMessage() {
|
||||||
|
|
||||||
|
if (!companionAppInstalled) {
|
||||||
|
return gs(R.string.insight_companion_app_not_installed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isConnected()) {
|
||||||
|
log("Not connected to companion");
|
||||||
|
if (Helpers.ratelimit("insight-app-not-connected", 5)) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((lastStatus == null) || (lastStatus != Status.INCOMPATIBLE)) {
|
||||||
|
if (compatabilityMessage != null) {
|
||||||
|
// if disconnected but previous state was incompatible
|
||||||
|
return compatabilityMessage;
|
||||||
|
} else {
|
||||||
|
return gs(R.string.insight_not_connected_to_companion_app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastStatus == null) {
|
||||||
|
return gs(R.string.insight_unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (lastStatus) {
|
||||||
|
case CONNECTED:
|
||||||
|
if (Helpers.msSince(lastStatusTime) > (60 * 10 * 1000)) {
|
||||||
|
tryToGetPumpStatusAgain();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case INCOMPATIBLE:
|
||||||
|
return statusToString(lastStatus) + " " + gs(R.string.insight_needs) + " " + getLocalVersion();
|
||||||
|
}
|
||||||
|
return statusToString(lastStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNiceLastStatusTime() {
|
||||||
|
if (lastStatusTime < 1) {
|
||||||
|
return gs(R.string.insight_startup_uppercase);
|
||||||
|
} else {
|
||||||
|
return Helpers.niceTimeScalar(Helpers.msSince(lastStatusTime)) + " " + gs(R.string.ago);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean uiFresh() {
|
||||||
|
// todo check other changes
|
||||||
|
|
||||||
|
if (Helpers.msSince(lastStatusTime) < FRESH_MS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Helpers.msSince(LiveHistory.getStatusTime()) < FRESH_MS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("AccessStaticViaInstance")
|
||||||
|
public void tryToGetPumpStatusAgain() {
|
||||||
|
if (Helpers.ratelimit("insight-retry-status-request", 5)) {
|
||||||
|
try {
|
||||||
|
MainApp.getConfigBuilder().getCommandQueue().readStatus("Insight. Status missing", null);
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestHistorySync() {
|
||||||
|
requestHistorySync(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestHistoryReSync() {
|
||||||
|
requestHistoryReSync(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestHistorySync(long delay) {
|
||||||
|
if (Helpers.ratelimit("insight-history-sync-request", 10)) {
|
||||||
|
final Intent intent = new Intent(ACTION_START_SYNC);
|
||||||
|
sendBroadcastToCompanion(intent, delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestHistoryReSync(long delay) {
|
||||||
|
if (Helpers.ratelimit("insight-history-resync-request", 300)) {
|
||||||
|
final Intent intent = new Intent(ACTION_START_RESYNC);
|
||||||
|
sendBroadcastToCompanion(intent, delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendBroadcastToCompanion(final Intent intent, final long delay) {
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
final PowerManager.WakeLock wl = Helpers.getWakeLock("insight-companion-delay", 60000);
|
||||||
|
intent.setPackage(COMPANION_APP_PACKAGE);
|
||||||
|
intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
|
||||||
|
try {
|
||||||
|
if (delay > 0) {
|
||||||
|
|
||||||
|
Thread.sleep(delay);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
//
|
||||||
|
} finally {
|
||||||
|
Helpers.releaseWakeLock(wl);
|
||||||
|
}
|
||||||
|
MainApp.instance().sendBroadcast(intent);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean lastStatusRecent() {
|
||||||
|
return true; // TODO evaluate whether current
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight.events;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.events.Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by jamorham on 23/01/2018.
|
||||||
|
*/
|
||||||
|
public class EventInsightPumpCallback extends Event {
|
||||||
|
|
||||||
|
public UUID request_uuid;
|
||||||
|
public boolean success = false;
|
||||||
|
public String message = null;
|
||||||
|
public int response_id = -1;
|
||||||
|
|
||||||
|
public EventInsightPumpCallback() {
|
||||||
|
request_uuid = UUID.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Event: " + request_uuid + " success: " + success + " msg: " + message;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight.events;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.events.EventUpdateGui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by jamorham on 23/01/2018.
|
||||||
|
*/
|
||||||
|
public class EventInsightPumpUpdateGui extends EventUpdateGui {
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight.history;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import info.nightscout.utils.SP;
|
||||||
|
import sugar.free.sightparser.handling.HistoryBroadcast;
|
||||||
|
|
||||||
|
import static info.nightscout.androidaps.plugins.PumpInsight.history.PumpIdCache.updatePumpSerialNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by jamorham on 27/01/2018.
|
||||||
|
*
|
||||||
|
* Parse inbound logbook intents
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
class HistoryIntentAdapter {
|
||||||
|
|
||||||
|
private HistoryLogAdapter logAdapter = new HistoryLogAdapter();
|
||||||
|
|
||||||
|
private static Date getDateExtra(Intent intent, String name) {
|
||||||
|
return (Date) intent.getSerializableExtra(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void log(String msg) {
|
||||||
|
android.util.Log.e("HistoryIntentAdapter", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static long getRecordUniqueID(long pump_serial_number, long pump_record_id) {
|
||||||
|
updatePumpSerialNumber(pump_serial_number);
|
||||||
|
return (pump_serial_number * 10000000) + pump_record_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void processTBRIntent(Intent intent) {
|
||||||
|
|
||||||
|
final int pump_tbr_duration = intent.getIntExtra(HistoryBroadcast.EXTRA_DURATION, -1);
|
||||||
|
final int pump_tbr_percent = intent.getIntExtra(HistoryBroadcast.EXTRA_TBR_AMOUNT, -1);
|
||||||
|
final int pump_record_id = intent.getIntExtra(HistoryBroadcast.EXTRA_EVENT_NUMBER, -1);
|
||||||
|
final long pump_serial_number = Long.parseLong(intent.getStringExtra(HistoryBroadcast.EXTRA_PUMP_SERIAL_NUMBER));
|
||||||
|
final Date event_time = getDateExtra(intent, HistoryBroadcast.EXTRA_EVENT_TIME);
|
||||||
|
final Date start_time = getDateExtra(intent, HistoryBroadcast.EXTRA_START_TIME);
|
||||||
|
|
||||||
|
if ((pump_tbr_duration == -1) || (pump_tbr_percent == -1) || (pump_record_id == -1)) {
|
||||||
|
log("Invalid TBR record!!!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final long record_unique_id = getRecordUniqueID(pump_serial_number, pump_record_id);
|
||||||
|
|
||||||
|
// other sanity checks
|
||||||
|
log("Creating TBR record: " + pump_tbr_percent + "% " + pump_tbr_duration + "m" + " id:" + record_unique_id);
|
||||||
|
logAdapter.createTBRrecord(start_time, pump_tbr_percent, pump_tbr_duration, record_unique_id);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void processDeliveredBolusIntent(Intent intent) {
|
||||||
|
|
||||||
|
final String bolus_type = intent.getStringExtra(HistoryBroadcast.EXTRA_BOLUS_TYPE);
|
||||||
|
final int bolus_id = intent.getIntExtra(HistoryBroadcast.EXTRA_BOLUS_ID,-1);
|
||||||
|
final int pump_record_id = intent.getIntExtra(HistoryBroadcast.EXTRA_EVENT_NUMBER, -1);
|
||||||
|
final long pump_serial_number = Long.parseLong(intent.getStringExtra(HistoryBroadcast.EXTRA_PUMP_SERIAL_NUMBER));
|
||||||
|
final Date event_time = getDateExtra(intent, HistoryBroadcast.EXTRA_EVENT_TIME);
|
||||||
|
final Date start_time = getDateExtra(intent, HistoryBroadcast.EXTRA_START_TIME);
|
||||||
|
final float immediate_amount = intent.getFloatExtra(HistoryBroadcast.EXTRA_IMMEDIATE_AMOUNT, -1);
|
||||||
|
final float extended_insulin = intent.getFloatExtra(HistoryBroadcast.EXTRA_EXTENDED_AMOUNT, -1);
|
||||||
|
final int extended_minutes = intent.getIntExtra(HistoryBroadcast.EXTRA_DURATION, -1);
|
||||||
|
|
||||||
|
final long record_unique_id = getRecordUniqueID(pump_serial_number, bolus_id > -1 ? bolus_id : pump_record_id);
|
||||||
|
|
||||||
|
switch (bolus_type) {
|
||||||
|
case "STANDARD":
|
||||||
|
if (immediate_amount == -1) {
|
||||||
|
log("ERROR Standard bolus fails sanity check");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LiveHistory.setStatus(bolus_type + " BOLUS\n" + immediate_amount + "U ", event_time.getTime());
|
||||||
|
logAdapter.createStandardBolusRecord(start_time, immediate_amount, record_unique_id);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "EXTENDED":
|
||||||
|
if ((extended_insulin == -1) || (extended_minutes == -1)) {
|
||||||
|
log("ERROR: Extended bolus fails sanity check");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LiveHistory.setStatus(bolus_type + " BOLUS\n" + extended_insulin + "U over " + extended_minutes + " min, ", event_time.getTime());
|
||||||
|
logAdapter.createExtendedBolusRecord(start_time, extended_insulin, extended_minutes, record_unique_id);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "MULTIWAVE":
|
||||||
|
if ((immediate_amount == -1) || (extended_insulin == -1) || (extended_minutes == -1)) {
|
||||||
|
log("ERROR: Multiwave bolus fails sanity check");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LiveHistory.setStatus(bolus_type + " BOLUS\n" + immediate_amount + "U + " + extended_insulin + "U over " + extended_minutes + " min, ", event_time.getTime());
|
||||||
|
logAdapter.createStandardBolusRecord(start_time, immediate_amount, pump_serial_number + pump_record_id);
|
||||||
|
logAdapter.createExtendedBolusRecord(start_time, extended_insulin, extended_minutes, record_unique_id);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log("ERROR, UNKNWON BOLUS TYPE: " + bolus_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight.history;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.data.DetailedBolusInfo;
|
||||||
|
import info.nightscout.androidaps.db.ExtendedBolus;
|
||||||
|
import info.nightscout.androidaps.db.Source;
|
||||||
|
import info.nightscout.androidaps.db.TemporaryBasal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by jamorham on 27/01/2018.
|
||||||
|
*
|
||||||
|
* Write to the History Log
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
class HistoryLogAdapter {
|
||||||
|
|
||||||
|
void createTBRrecord(Date eventDate, int percent, int duration, long record_id) {
|
||||||
|
|
||||||
|
final TemporaryBasal temporaryBasal = new TemporaryBasal();
|
||||||
|
temporaryBasal.date = eventDate.getTime();
|
||||||
|
temporaryBasal.source = Source.PUMP;
|
||||||
|
temporaryBasal.pumpId = record_id;
|
||||||
|
temporaryBasal.percentRate = percent;
|
||||||
|
temporaryBasal.durationInMinutes = duration;
|
||||||
|
|
||||||
|
MainApp.getConfigBuilder().addToHistoryTempBasal(temporaryBasal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void createExtendedBolusRecord(Date eventDate, double insulin, int durationInMinutes, long record_id) {
|
||||||
|
|
||||||
|
// TODO trap items below minimum period
|
||||||
|
|
||||||
|
final ExtendedBolus extendedBolus = new ExtendedBolus();
|
||||||
|
extendedBolus.date = eventDate.getTime();
|
||||||
|
extendedBolus.insulin = insulin;
|
||||||
|
extendedBolus.durationInMinutes = durationInMinutes;
|
||||||
|
extendedBolus.source = Source.PUMP;
|
||||||
|
extendedBolus.pumpId = record_id;
|
||||||
|
|
||||||
|
MainApp.getConfigBuilder().addToHistoryExtendedBolus(extendedBolus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void createStandardBolusRecord(Date eventDate, double insulin, long record_id) {
|
||||||
|
|
||||||
|
//DetailedBolusInfo detailedBolusInfo = DetailedBolusInfoStorage.findDetailedBolusInfo(eventDate.getTime());
|
||||||
|
|
||||||
|
final DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo();
|
||||||
|
detailedBolusInfo.date = eventDate.getTime();
|
||||||
|
detailedBolusInfo.source = Source.PUMP;
|
||||||
|
detailedBolusInfo.pumpId = record_id;
|
||||||
|
detailedBolusInfo.insulin = insulin;
|
||||||
|
MainApp.getConfigBuilder().addToHistoryTreatment(detailedBolusInfo);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight.history;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
|
||||||
|
import static info.nightscout.androidaps.plugins.PumpInsight.history.HistoryReceiver.Status.BUSY;
|
||||||
|
import static info.nightscout.androidaps.plugins.PumpInsight.history.HistoryReceiver.Status.SYNCED;
|
||||||
|
import static info.nightscout.androidaps.plugins.PumpInsight.history.HistoryReceiver.Status.SYNCING;
|
||||||
|
import static sugar.free.sightparser.handling.HistoryBroadcast.ACTION_BOLUS_DELIVERED;
|
||||||
|
import static sugar.free.sightparser.handling.HistoryBroadcast.ACTION_BOLUS_PROGRAMMED;
|
||||||
|
import static sugar.free.sightparser.handling.HistoryBroadcast.ACTION_END_OF_TBR;
|
||||||
|
import static sugar.free.sightparser.handling.HistoryBroadcast.ACTION_PUMP_STATUS_CHANGED;
|
||||||
|
import static sugar.free.sightparser.handling.HistoryBroadcast.ACTION_STILL_SYNCING;
|
||||||
|
import static sugar.free.sightparser.handling.HistoryBroadcast.ACTION_SYNC_FINISHED;
|
||||||
|
import static sugar.free.sightparser.handling.HistoryBroadcast.ACTION_SYNC_STARTED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by jamorham on 27/01/2018.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class HistoryReceiver {
|
||||||
|
|
||||||
|
private static BroadcastReceiver historyReceiver;
|
||||||
|
private volatile static Status status = Status.IDLE;
|
||||||
|
private volatile HistoryIntentAdapter intentAdapter;
|
||||||
|
|
||||||
|
public HistoryReceiver() {
|
||||||
|
initializeHistoryReceiver();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized void registerHistoryReceiver() {
|
||||||
|
try {
|
||||||
|
MainApp.instance().unregisterReceiver(historyReceiver);
|
||||||
|
} catch (Exception e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
final IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(ACTION_PUMP_STATUS_CHANGED);
|
||||||
|
filter.addAction(ACTION_BOLUS_PROGRAMMED);
|
||||||
|
filter.addAction(ACTION_BOLUS_DELIVERED);
|
||||||
|
filter.addAction(ACTION_END_OF_TBR);
|
||||||
|
filter.addAction(ACTION_SYNC_STARTED);
|
||||||
|
filter.addAction(ACTION_STILL_SYNCING);
|
||||||
|
filter.addAction(ACTION_SYNC_FINISHED);
|
||||||
|
|
||||||
|
MainApp.instance().registerReceiver(historyReceiver, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// History
|
||||||
|
|
||||||
|
private static void log(String msg) {
|
||||||
|
android.util.Log.e("INSIGHTPUMPHR", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getStatusString() {
|
||||||
|
return status.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void initializeHistoryReceiver() {
|
||||||
|
historyReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, final Intent intent) {
|
||||||
|
|
||||||
|
final String action = intent.getAction();
|
||||||
|
if (action == null) return;
|
||||||
|
|
||||||
|
if (intentAdapter == null) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (intentAdapter == null) {
|
||||||
|
intentAdapter = new HistoryIntentAdapter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
|
||||||
|
case ACTION_SYNC_STARTED:
|
||||||
|
status = SYNCING;
|
||||||
|
break;
|
||||||
|
case ACTION_STILL_SYNCING:
|
||||||
|
status = BUSY;
|
||||||
|
break;
|
||||||
|
case ACTION_SYNC_FINISHED:
|
||||||
|
status = SYNCED;
|
||||||
|
break;
|
||||||
|
case ACTION_BOLUS_DELIVERED:
|
||||||
|
intentAdapter.processDeliveredBolusIntent(intent);
|
||||||
|
break;
|
||||||
|
case ACTION_END_OF_TBR:
|
||||||
|
intentAdapter.processTBRIntent(intent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Status {
|
||||||
|
IDLE(R.string.insight_history_idle),
|
||||||
|
SYNCING(R.string.insight_history_syncing),
|
||||||
|
BUSY(R.string.insight_history_busy),
|
||||||
|
SYNCED(R.string.insight_history_synced);
|
||||||
|
|
||||||
|
private final int string_id;
|
||||||
|
|
||||||
|
Status(int string_id) {
|
||||||
|
this.string_id = string_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MainApp.instance().getString(string_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight.history;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by jamorham on 27/01/2018.
|
||||||
|
*
|
||||||
|
* In memory status storage class
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class LiveHistory {
|
||||||
|
|
||||||
|
private static String status = "";
|
||||||
|
private static long status_time = -1;
|
||||||
|
|
||||||
|
public static String getStatus() {
|
||||||
|
if (status.equals("")) return status;
|
||||||
|
return status + " " + Helpers.niceTimeScalar(Helpers.msSince(status_time)) + " " + gs(R.string.ago);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getStatusTime() {
|
||||||
|
return status_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setStatus(String mystatus, long eventtime) {
|
||||||
|
if (eventtime > status_time) {
|
||||||
|
status_time = eventtime;
|
||||||
|
status = mystatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String gs(int id) {
|
||||||
|
return MainApp.instance().getString(id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight.history;
|
||||||
|
|
||||||
|
import info.nightscout.utils.SP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by jamorham on 01/02/2018.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class PumpIdCache {
|
||||||
|
|
||||||
|
private static final String INSIGHT_PUMP_ID_PREF = "insight-pump-id";
|
||||||
|
private static long cachedPumpSerialNumber = -1;
|
||||||
|
|
||||||
|
private static void log(String msg) {
|
||||||
|
android.util.Log.e("PumpIdCache", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void updatePumpSerialNumber(long pump_serial_number) {
|
||||||
|
if (pump_serial_number != cachedPumpSerialNumber) {
|
||||||
|
cachedPumpSerialNumber = pump_serial_number;
|
||||||
|
log("Updating pump serial number: " + pump_serial_number);
|
||||||
|
SP.putLong(INSIGHT_PUMP_ID_PREF, cachedPumpSerialNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getRecordUniqueID(long record_id) {
|
||||||
|
if (cachedPumpSerialNumber == -1) {
|
||||||
|
cachedPumpSerialNumber = SP.getLong(INSIGHT_PUMP_ID_PREF, 0L);
|
||||||
|
}
|
||||||
|
return HistoryIntentAdapter.getRecordUniqueID(cachedPumpSerialNumber, record_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,172 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.text.DecimalFormatSymbols;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by jamorham on 24/01/2018.
|
||||||
|
*
|
||||||
|
* Useful utility methods from xDrip+
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Helpers {
|
||||||
|
|
||||||
|
private static final String TAG = "InsightHelpers";
|
||||||
|
|
||||||
|
private static final Map<String, Long> rateLimits = new HashMap<>();
|
||||||
|
// singletons to avoid repeated allocation
|
||||||
|
private static DecimalFormatSymbols dfs;
|
||||||
|
private static DecimalFormat df;
|
||||||
|
|
||||||
|
// return true if below rate limit
|
||||||
|
public static synchronized boolean ratelimit(String name, int seconds) {
|
||||||
|
// check if over limit
|
||||||
|
if ((rateLimits.containsKey(name)) && (tsl() - rateLimits.get(name) < (seconds * 1000))) {
|
||||||
|
Log.d(TAG, name + " rate limited: " + seconds + " seconds");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// not over limit
|
||||||
|
rateLimits.put(name, tsl());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long tsl() {
|
||||||
|
return System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long msSince(long when) {
|
||||||
|
return (tsl() - when);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long msTill(long when) {
|
||||||
|
return (when - tsl());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean checkPackageExists(Context context, String TAG, String packageName) {
|
||||||
|
try {
|
||||||
|
final PackageManager pm = context.getPackageManager();
|
||||||
|
final PackageInfo pi = pm.getPackageInfo(packageName, 0);
|
||||||
|
return pi.packageName.equals(packageName);
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.wtf(TAG, "Exception trying to determine packages! " + e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean runOnUiThreadDelayed(Runnable theRunnable, long delay) {
|
||||||
|
return new Handler(MainApp.instance().getMainLooper()).postDelayed(theRunnable, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PowerManager.WakeLock getWakeLock(final String name, int millis) {
|
||||||
|
final PowerManager pm = (PowerManager) MainApp.instance().getSystemService(Context.POWER_SERVICE);
|
||||||
|
if (pm == null) return null;
|
||||||
|
final PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
|
||||||
|
wl.acquire(millis);
|
||||||
|
return wl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void releaseWakeLock(PowerManager.WakeLock wl) {
|
||||||
|
if (wl == null) return;
|
||||||
|
if (wl.isHeld()) wl.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String niceTimeSince(long t) {
|
||||||
|
return niceTimeScalar(msSince(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String niceTimeTill(long t) {
|
||||||
|
return niceTimeScalar(-msSince(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String niceTimeScalar(long t) {
|
||||||
|
String unit = gs(R.string.second);
|
||||||
|
t = t / 1000;
|
||||||
|
if (t > 59) {
|
||||||
|
unit = gs(R.string.minute);
|
||||||
|
t = t / 60;
|
||||||
|
if (t > 59) {
|
||||||
|
unit = gs(R.string.hour);
|
||||||
|
t = t / 60;
|
||||||
|
if (t > 24) {
|
||||||
|
unit = gs(R.string.day);
|
||||||
|
t = t / 24;
|
||||||
|
if (t > 28) {
|
||||||
|
unit = gs(R.string.week);
|
||||||
|
t = t / 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (t != 1) unit = unit + gs(R.string.time_plural);
|
||||||
|
return qs((double) t, 0) + " " + unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String gs(int id) {
|
||||||
|
return MainApp.instance().getString(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String qs(double x, int digits) {
|
||||||
|
|
||||||
|
if (digits == -1) {
|
||||||
|
digits = 0;
|
||||||
|
if (((int) x != x)) {
|
||||||
|
digits++;
|
||||||
|
if ((((int) x * 10) / 10 != x)) {
|
||||||
|
digits++;
|
||||||
|
if ((((int) x * 100) / 100 != x)) digits++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dfs == null) {
|
||||||
|
final DecimalFormatSymbols local_dfs = new DecimalFormatSymbols();
|
||||||
|
local_dfs.setDecimalSeparator('.');
|
||||||
|
dfs = local_dfs; // avoid race condition
|
||||||
|
}
|
||||||
|
|
||||||
|
final DecimalFormat this_df;
|
||||||
|
// use singleton if on ui thread otherwise allocate new as DecimalFormat is not thread safe
|
||||||
|
if (Thread.currentThread().getId() == 1) {
|
||||||
|
if (df == null) {
|
||||||
|
final DecimalFormat local_df = new DecimalFormat("#", dfs);
|
||||||
|
local_df.setMinimumIntegerDigits(1);
|
||||||
|
df = local_df; // avoid race condition
|
||||||
|
}
|
||||||
|
this_df = df;
|
||||||
|
} else {
|
||||||
|
this_df = new DecimalFormat("#", dfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
this_df.setMaximumFractionDigits(digits);
|
||||||
|
return this_df.format(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String niceTimeScalarRedux(long t) {
|
||||||
|
return niceTimeScalar(t).replaceFirst("^1 ", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double roundDouble(double value, int places) {
|
||||||
|
if (places < 0) throw new IllegalArgumentException("Invalid decimal places");
|
||||||
|
BigDecimal bd = new BigDecimal(value);
|
||||||
|
bd = bd.setScale(places, RoundingMode.HALF_UP);
|
||||||
|
return bd.doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by jamorham on 26/01/2018.
|
||||||
|
*
|
||||||
|
* For representing row status items
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class StatusItem {
|
||||||
|
|
||||||
|
public enum Highlight {
|
||||||
|
NORMAL,
|
||||||
|
GOOD,
|
||||||
|
BAD,
|
||||||
|
NOTICE,
|
||||||
|
CRITICAL
|
||||||
|
}
|
||||||
|
|
||||||
|
public String name;
|
||||||
|
public String value;
|
||||||
|
public Highlight highlight;
|
||||||
|
public String button_name;
|
||||||
|
public Runnable runnable;
|
||||||
|
|
||||||
|
|
||||||
|
public StatusItem(String name, String value) {
|
||||||
|
this(name, value, Highlight.NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusItem() {
|
||||||
|
this("line-break", "", Highlight.NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusItem(String name, Highlight highlight) {
|
||||||
|
this("heading-break", name, highlight);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusItem(String name, Runnable runnable) {
|
||||||
|
this("button-break", "", Highlight.NORMAL, name, runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusItem(String name, String value, Highlight highlight) {
|
||||||
|
this(name, value, highlight, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusItem(String name, String value, Highlight highlight, String button_name, Runnable runnable) {
|
||||||
|
this.name = name;
|
||||||
|
this.value = value;
|
||||||
|
this.highlight = highlight;
|
||||||
|
this.button_name = button_name;
|
||||||
|
this.runnable = runnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusItem(String name, Integer value) {
|
||||||
|
this(name, value, Highlight.NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusItem(String name, Integer value, Highlight highlight) {
|
||||||
|
this.name = name;
|
||||||
|
this.value = Integer.toString(value);
|
||||||
|
this.highlight = highlight;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight.utils.ui;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpInsight.utils.StatusItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by jamorham on 26/01/2018.
|
||||||
|
*
|
||||||
|
* Convert StatusItem to View
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class StatusItemViewAdapter {
|
||||||
|
|
||||||
|
private final Activity activity;
|
||||||
|
private final ViewGroup holder;
|
||||||
|
|
||||||
|
public StatusItemViewAdapter(Activity activity, ViewGroup holder) {
|
||||||
|
this.activity = activity;
|
||||||
|
this.holder = holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public View inflateStatus(StatusItem statusItem) {
|
||||||
|
if (activity == null) return null;
|
||||||
|
final View child = activity.getLayoutInflater().inflate(R.layout.insightpump_statuselements, null);
|
||||||
|
final TextView name = (TextView) child.findViewById(R.id.insightstatuslabel);
|
||||||
|
final TextView value = (TextView)child.findViewById(R.id.insightstatusvalue);
|
||||||
|
final TextView spacer = (TextView)child.findViewById(R.id.insightstatusspacer);
|
||||||
|
final LinearLayout layout = (LinearLayout)child.findViewById(R.id.insightstatuslayout);
|
||||||
|
|
||||||
|
if (statusItem.name.equals("line-break")) {
|
||||||
|
spacer.setVisibility(View.GONE);
|
||||||
|
name.setVisibility(View.GONE);
|
||||||
|
value.setVisibility(View.GONE);
|
||||||
|
layout.setPadding(10, 10, 10, 10);
|
||||||
|
} else if (statusItem.name.equals("heading-break")) {
|
||||||
|
value.setVisibility(View.GONE);
|
||||||
|
spacer.setVisibility(View.GONE);
|
||||||
|
name.setText(statusItem.value);
|
||||||
|
name.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||||
|
name.setTextColor(Color.parseColor("#fff9c4"));
|
||||||
|
} else {
|
||||||
|
name.setText(statusItem.name);
|
||||||
|
value.setText(statusItem.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int this_color = getHighlightColor(statusItem);
|
||||||
|
name.setBackgroundColor(this_color);
|
||||||
|
value.setBackgroundColor(this_color);
|
||||||
|
spacer.setBackgroundColor(this_color);
|
||||||
|
|
||||||
|
if (this_color != Color.TRANSPARENT) {
|
||||||
|
name.setTextColor(Color.WHITE);
|
||||||
|
spacer.setTextColor(Color.WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (holder != null) {
|
||||||
|
holder.addView(child);
|
||||||
|
}
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getHighlightColor(StatusItem row) {
|
||||||
|
switch (row.highlight) {
|
||||||
|
case BAD:
|
||||||
|
return Color.parseColor("#480000");
|
||||||
|
case NOTICE:
|
||||||
|
return Color.parseColor("#403000");
|
||||||
|
case GOOD:
|
||||||
|
return Color.parseColor("#003000");
|
||||||
|
case CRITICAL:
|
||||||
|
return Color.parseColor("#770000");
|
||||||
|
default:
|
||||||
|
return Color.TRANSPARENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
app/src/main/res/layout/insightpump_fragment.xml
Normal file
37
app/src/main/res/layout/insightpump_fragment.xml
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context="info.nightscout.androidaps.plugins.PumpInsight.InsightPumpFragment">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/insighttopholder"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_marginBottom="15dp"
|
||||||
|
android:text="@string/insightpump"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/insightholder"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</FrameLayout>
|
60
app/src/main/res/layout/insightpump_statuselements.xml
Normal file
60
app/src/main/res/layout/insightpump_statuselements.xml
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/insightstatusmasterlayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/insightstatuslayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingBottom="5dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/insightstatuslabel"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="end"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:text="Label"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/insightstatusspacer"
|
||||||
|
android:layout_width="5dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:paddingEnd="2dp"
|
||||||
|
android:paddingStart="2dp"
|
||||||
|
android:text=":"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/insightstatusvalue"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="start"
|
||||||
|
android:paddingLeft="5dp"
|
||||||
|
android:text="Value"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="2dip"
|
||||||
|
android:layout_marginBottom="5dp"
|
||||||
|
android:layout_marginLeft="20dp"
|
||||||
|
android:layout_marginRight="20dp"
|
||||||
|
android:layout_marginTop="0dp"
|
||||||
|
android:background="@color/listdelimiter" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
|
@ -861,5 +861,45 @@
|
||||||
<string name="combo_error_bolus_recovery_progress">Recovering from connection loss</string>
|
<string name="combo_error_bolus_recovery_progress">Recovering from connection loss</string>
|
||||||
<string name="combo_reservoir_level_insufficient_for_bolus">Not enough insulin for bolus left in reservoir</string>
|
<string name="combo_reservoir_level_insufficient_for_bolus">Not enough insulin for bolus left in reservoir</string>
|
||||||
<string name="extendedbolusdeliveryerror">Extended bolus delivery error</string>
|
<string name="extendedbolusdeliveryerror">Extended bolus delivery error</string>
|
||||||
|
<string name="insightpump_shortname">Insight</string>
|
||||||
|
<string name="insightpump">Insight Pump</string>
|
||||||
|
<string name="status_no_colon">Status</string>
|
||||||
|
<string name="changed">Changed</string>
|
||||||
|
<string name="pump_stopped_uppercase">PUMP STOPPED</string>
|
||||||
|
<string name="status_updated">Status Updated</string>
|
||||||
|
<string name="ago">ago</string>
|
||||||
|
<string name="with">with</string>
|
||||||
|
<string name="insight_active_tbr">Active TBR</string>
|
||||||
|
<string name="insight_min_left">min left</string>
|
||||||
|
<string name="log_book">Log book</string>
|
||||||
|
<string name="insight_last_completed_action">Last Completed Action</string>
|
||||||
|
<string name="insight_min">min</string>
|
||||||
|
<string name="insight_remaining_over">remaining over</string>
|
||||||
|
<string name="insight_total_with">total with</string>
|
||||||
|
<string name="insight_upfront_with">upfront with</string>
|
||||||
|
<string name="insight_stay_always_connected">Stay always connected</string>
|
||||||
|
<string name="insight_use_real_tbr_cancels">Use Real TBR cancels</string>
|
||||||
|
<string name="insight_actually_cancel_tbr_summary">Actually cancel a TBR (creates pump alarm) instead of setting 90% or 110% TBR for 15 minutes</string>
|
||||||
|
<string name="insight_history_idle">IDLE</string>
|
||||||
|
<string name="insight_history_syncing">SYNCING</string>
|
||||||
|
<string name="insight_history_busy">BUSY</string>
|
||||||
|
<string name="insight_history_synced">SYNCED</string>
|
||||||
|
<string name="insight_startup_uppercase">STARTUP</string>
|
||||||
|
<string name="insight_needs">needs</string>
|
||||||
|
<string name="insight_not_connected_to_companion_app">Not connected to companion app!</string>
|
||||||
|
<string name="insight_companion_app_not_installed">Companion app does not appear to be installed!</string>
|
||||||
|
<string name="insight_incompatible_compantion_app_we_need_version">Incompatible companion app, we need version</string>
|
||||||
|
<string name="insight_unknown">Unknown</string>
|
||||||
|
<string name="insight_waiting_for_code">Waiting for code confirmation</string>
|
||||||
|
<string name="insight_code_rejected">Code rejected</string>
|
||||||
|
<string name="insight_app_binding">App binding</string>
|
||||||
|
<string name="insight_not_authorized">Not authorized</string>
|
||||||
|
<string name="insight_incompatible">Incompatible</string>
|
||||||
|
<string name="second">second</string>
|
||||||
|
<string name="minute">minute</string>
|
||||||
|
<string name="hour">hour</string>
|
||||||
|
<string name="day">day</string>
|
||||||
|
<string name="week">week</string>
|
||||||
|
<string name="time_plural">s</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
|
|
16
app/src/main/res/xml/pref_insightpump.xml
Normal file
16
app/src/main/res/xml/pref_insightpump.xml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="insightpump"
|
||||||
|
android:title="@string/insightpump">
|
||||||
|
</PreferenceCategory>
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="insight_always_connected"
|
||||||
|
android:title="@string/insight_stay_always_connected" />
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="insight_real_tbr_cancel"
|
||||||
|
android:title="@string/insight_use_real_tbr_cancels"
|
||||||
|
android:summary="@string/insight_actually_cancel_tbr_summary"/>
|
||||||
|
</PreferenceScreen >
|
|
@ -0,0 +1,31 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpInsight;
|
||||||
|
|
||||||
|
|
||||||
|
import com.google.common.truth.Truth;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers.roundDouble;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by jamorham on 26.01.2018.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class HelpersTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkRounding() throws Exception {
|
||||||
|
|
||||||
|
// TODO more test cases including known precision breakdowns
|
||||||
|
|
||||||
|
Truth.assertThat(roundDouble(Double.parseDouble("0.999999"),0))
|
||||||
|
.isEqualTo(1d);
|
||||||
|
|
||||||
|
Truth.assertThat(roundDouble(Double.parseDouble("0.123456"),0))
|
||||||
|
.isEqualTo(0d);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue