Merge branch 'omnipod_eros_andy' into omnipod_eros_bart

This commit is contained in:
Bart Sopers 2019-08-11 15:56:45 +02:00
commit ca325c18e9
34 changed files with 3364 additions and 118 deletions

View file

@ -46,6 +46,7 @@
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
@ -79,33 +80,33 @@
<!-- Receive new BG readings from other local apps -->
<receiver
android:name=".receivers.DataReceiver"
android:enabled="true"
android:exported="true">
android:name=".receivers.DataReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<!-- Receiver from xDrip -->
<action android:name="com.eveningoutpost.dexdrip.BgEstimate"/>
<action android:name="com.eveningoutpost.dexdrip.BgEstimate" />
<!-- Receiver from 640g uploader -->
<action android:name="com.eveningoutpost.dexdrip.NS_EMULATOR"/>
<action android:name="com.eveningoutpost.dexdrip.NS_EMULATOR" />
<!-- Receiver from glimp -->
<action android:name="it.ct.glicemia.ACTION_GLUCOSE_MEASURED"/>
<action android:name="it.ct.glicemia.ACTION_GLUCOSE_MEASURED" />
<!-- Receiver from Dexcom -->
<action android:name="com.dexcom.cgm.EXTERNAL_BROADCAST"/>
<action android:name="com.dexcom.cgm.EXTERNAL_BROADCAST" />
<!-- Receiver from Poctech -->
<action android:name="com.china.poctech.data"/>
<action android:name="com.china.poctech.data" />
<!-- Receiver from Tomato -->
<action android:name="com.fanqies.tomatofn.BgEstimate"/>
<action android:name="com.fanqies.tomatofn.BgEstimate" />
</intent-filter>
</receiver>
<!-- Receive new SMS messages -->
<receiver
android:name=".receivers.SmsReceiver"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BROADCAST_SMS">
android:name=".receivers.SmsReceiver"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BROADCAST_SMS">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
@ -285,7 +286,8 @@
<service
android:name=".plugins.pump.medtronic.service.RileyLinkMedtronicService"
android:enabled="true"
android:exported="false" />
android:exported="true" />
<activity android:name=".plugins.pump.common.dialog.RileyLinkBLEScanActivity">
<intent-filter>
<action android:name="info.nightscout.androidaps.plugins.PumpCommon.dialog.RileyLinkBLEScanActivity" />
@ -298,7 +300,16 @@
android:theme="@style/Theme.AppCompat.NoTitle" />
<activity android:name=".plugins.pump.medtronic.dialog.MedtronicHistoryActivity" />
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<!-- Omnipod service and activities -->
<service
android:name=".plugins.pump.omnipod.service.RileyLinkOmnipodService"
android:enabled="true"
android:exported="true" />
<uses-library
android:name="org.apache.http.legacy"
android:required="false" />
</application>

View file

@ -1,14 +1,10 @@
package info.nightscout.androidaps;
import android.app.Application;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.SystemClock;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.PluralsRes;
@ -70,8 +66,6 @@ import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin;
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin;
import info.nightscout.androidaps.plugins.profile.simple.SimpleProfilePlugin;
import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil;
import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin;
import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin;
import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin;
@ -79,6 +73,7 @@ import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin;
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin;
import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin;
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin;
import info.nightscout.androidaps.plugins.pump.omnipod.OmnipodPumpPlugin;
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin;
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin;
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin;
@ -190,7 +185,10 @@ public class MainApp extends Application {
if (Config.PUMPDRIVERS) pluginsList.add(LocalInsightPlugin.getPlugin());
pluginsList.add(CareportalPlugin.getPlugin());
if (Config.PUMPDRIVERS) pluginsList.add(ComboPlugin.getPlugin());
if (Config.PUMPDRIVERS && engineeringMode) pluginsList.add(MedtronicPumpPlugin.getPlugin());
if (Config.PUMPDRIVERS && engineeringMode)
pluginsList.add(MedtronicPumpPlugin.getPlugin());
if (Config.PUMPDRIVERS && engineeringMode)
pluginsList.add(OmnipodPumpPlugin.getPlugin());
if (Config.MDI) pluginsList.add(MDIPlugin.getPlugin());
pluginsList.add(VirtualPumpPlugin.getPlugin());
if (Config.APS) pluginsList.add(LoopPlugin.getPlugin());
@ -451,7 +449,7 @@ public class MainApp extends Application {
unregisterReceiver(btReceiver);
}
if (timeDateOrTZChangeReceiver!=null) {
if (timeDateOrTZChangeReceiver != null) {
unregisterReceiver(timeDateOrTZChangeReceiver);
}

View file

@ -23,6 +23,7 @@ import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.FabricPrivacy;
import info.nightscout.androidaps.utils.MidnightTime;
import info.nightscout.androidaps.utils.Round;
public class Profile {
private static Logger log = LoggerFactory.getLogger(Profile.class);
@ -475,6 +476,17 @@ public class Profile {
public int timeAsSeconds;
public double value;
public boolean equals(Object otherObject) {
if (!(otherObject instanceof ProfileValue)) {
return false;
}
ProfileValue otherProfileValue = (ProfileValue) otherObject;
return (timeAsSeconds == otherProfileValue.timeAsSeconds) && Round.isSame(value, otherProfileValue.value);
}
}
public synchronized ProfileValue[] getBasalValues() {
@ -665,4 +677,27 @@ public class Profile {
public int getTimeshift() {
return timeshift;
}
public boolean isProfileTheSame(Profile otherProfile) {
if (!Round.isSame(this.baseBasalSum(), otherProfile.baseBasalSum()))
return false;
ProfileValue[] basalValues = this.getBasalValues();
ProfileValue[] otherBasalValues = otherProfile.getBasalValues();
if (basalValues.length != otherBasalValues.length)
return false;
for (int i = 0; i < basalValues.length; i++) {
if (!basalValues[i].equals(otherBasalValues[i])) {
return false;
}
}
return true;
}
}

View file

@ -24,6 +24,7 @@ import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.db.ExtendedBolus;
import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.events.EventAppExit;
import info.nightscout.androidaps.events.EventCustomActionsChanged;
import info.nightscout.androidaps.interfaces.ConstraintsInterface;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PluginDescription;
@ -31,6 +32,7 @@ import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.PumpDescription;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.common.ManufacturerType;
import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress;
import info.nightscout.androidaps.plugins.pump.common.data.PumpStatus;
import info.nightscout.androidaps.plugins.pump.common.defs.PumpDriverState;
@ -51,9 +53,9 @@ public abstract class PumpPluginAbstract extends PluginBase implements PumpInter
private static final Logger LOG = LoggerFactory.getLogger(L.PUMP);
protected static final PumpEnactResult OPERATION_NOT_SUPPORTED = new PumpEnactResult().success(false)
.enacted(false).comment(MainApp.gs(R.string.pump_operation_not_supported_by_pump_driver));
.enacted(false).comment(MainApp.gs(R.string.pump_operation_not_supported_by_pump_driver));
protected static final PumpEnactResult OPERATION_NOT_YET_SUPPORTED = new PumpEnactResult().success(false)
.enacted(false).comment(MainApp.gs(R.string.pump_operation_not_yet_supported_by_pump));
.enacted(false).comment(MainApp.gs(R.string.pump_operation_not_yet_supported_by_pump));
protected PumpDescription pumpDescription = new PumpDescription();
protected PumpStatus pumpStatus;
@ -238,7 +240,7 @@ public abstract class PumpPluginAbstract extends PluginBase implements PumpInter
@Override
public PumpEnactResult setTempBasalAbsolute(Double absoluteRate, Integer durationInMinutes, Profile profile,
boolean enforceNew) {
boolean enforceNew) {
if (isLoggingEnabled())
LOG.warn("setTempBasalAbsolute [PumpPluginAbstract] - Not implemented.");
return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver);
@ -247,7 +249,7 @@ public abstract class PumpPluginAbstract extends PluginBase implements PumpInter
@Override
public PumpEnactResult setTempBasalPercent(Integer percent, Integer durationInMinutes, Profile profile,
boolean enforceNew) {
boolean enforceNew) {
if (isLoggingEnabled())
LOG.warn("setTempBasalPercent [PumpPluginAbstract] - Not implemented.");
return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver);
@ -301,8 +303,8 @@ public abstract class PumpPluginAbstract extends PluginBase implements PumpInter
// Short info for SMS, Wear etc
public boolean isFakingTempsByExtendedBoluses() {
if (isLoggingEnabled())
LOG.warn("isFakingTempsByExtendedBoluses [PumpPluginAbstract] - Not implemented.");
//if (isLoggingEnabled())
// LOG.warn("isFakingTempsByExtendedBoluses [PumpPluginAbstract] - Not implemented.");
return false;
}
@ -339,7 +341,7 @@ public abstract class PumpPluginAbstract extends PluginBase implements PumpInter
TemporaryBasal tb = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(System.currentTimeMillis());
if (tb != null) {
extended.put("TempBasalAbsoluteRate",
tb.tempBasalConvertedToAbsolute(System.currentTimeMillis(), profile));
tb.tempBasalConvertedToAbsolute(System.currentTimeMillis(), profile));
extended.put("TempBasalStart", DateUtil.dateAndTimeString(tb.date));
extended.put("TempBasalRemaining", tb.getPlannedRemainingMinutes());
}
@ -371,20 +373,20 @@ public abstract class PumpPluginAbstract extends PluginBase implements PumpInter
String ret = "";
if (pumpStatus.lastConnection != 0) {
Long agoMsec = System.currentTimeMillis() - pumpStatus.lastConnection;
int agoMin = (int)(agoMsec / 60d / 1000d);
int agoMin = (int) (agoMsec / 60d / 1000d);
ret += "LastConn: " + agoMin + " min ago\n";
}
if (pumpStatus.lastBolusTime != null && pumpStatus.lastBolusTime.getTime() != 0) {
ret += "LastBolus: " + DecimalFormatter.to2Decimal(pumpStatus.lastBolusAmount) + "U @" + //
android.text.format.DateFormat.format("HH:mm", pumpStatus.lastBolusTime) + "\n";
android.text.format.DateFormat.format("HH:mm", pumpStatus.lastBolusTime) + "\n";
}
TemporaryBasal activeTemp = TreatmentsPlugin.getPlugin()
.getRealTempBasalFromHistory(System.currentTimeMillis());
.getRealTempBasalFromHistory(System.currentTimeMillis());
if (activeTemp != null) {
ret += "Temp: " + activeTemp.toStringFull() + "\n";
}
ExtendedBolus activeExtendedBolus = TreatmentsPlugin.getPlugin().getExtendedBolusFromHistory(
System.currentTimeMillis());
System.currentTimeMillis());
if (activeExtendedBolus != null) {
ret += "Extended: " + activeExtendedBolus.toString() + "\n";
}
@ -408,7 +410,7 @@ public abstract class PumpPluginAbstract extends PluginBase implements PumpInter
if (isLoggingEnabled())
LOG.error("deliverTreatment: Invalid input");
return new PumpEnactResult().success(false).enacted(false).bolusDelivered(0d).carbsDelivered(0d)
.comment(MainApp.gs(R.string.danar_invalidinput));
.comment(MainApp.gs(R.string.danar_invalidinput));
} else if (detailedBolusInfo.insulin > 0) {
// bolus needed, ask pump to deliver it
return deliverBolus(detailedBolusInfo);
@ -426,7 +428,7 @@ public abstract class PumpPluginAbstract extends PluginBase implements PumpInter
LOG.debug("deliverTreatment: Carb only treatment.");
return new PumpEnactResult().success(true).enacted(true).bolusDelivered(0d)
.carbsDelivered(detailedBolusInfo.carbs).comment(MainApp.gs(R.string.virtualpump_resultok));
.carbsDelivered(detailedBolusInfo.carbs).comment(MainApp.gs(R.string.virtualpump_resultok));
}
} finally {
triggerUIChange();
@ -450,4 +452,32 @@ public abstract class PumpPluginAbstract extends PluginBase implements PumpInter
return new PumpEnactResult().success(false).enacted(false).comment(MainApp.gs(resourceId));
}
}
@Override
public boolean canHandleDST() {
return false;
}
@Override
public ManufacturerType manufacturer() {
return pumpStatus.pumpType.getManufacturer();
}
@Override
public PumpType model() {
return pumpStatus.pumpType;
}
@Override
public void timeDateOrTimeZoneChanged() {
}
public void refreshCustomActionsList() {
MainApp.bus().post(new EventCustomActionsChanged());
}
}

View file

@ -0,0 +1,79 @@
package info.nightscout.androidaps.plugins.pump.common.data;
import com.google.gson.annotations.Expose;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.logging.L;
public class TempBasalPair {
private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM);
@Expose
protected double insulinRate = 0.0d;
@Expose
protected int durationMinutes = 0;
@Expose
protected boolean isPercent = false;
private Long start;
private Long end;
public TempBasalPair() {
}
public TempBasalPair(double insulinRate, boolean isPercent, int durationMinutes) {
this.insulinRate = insulinRate;
this.isPercent = isPercent;
this.durationMinutes = durationMinutes;
}
public double getInsulinRate() {
return insulinRate;
}
public void setInsulinRate(double insulinRate) {
this.insulinRate = insulinRate;
}
public int getDurationMinutes() {
return durationMinutes;
}
public void setDurationMinutes(int durationMinutes) {
this.durationMinutes = durationMinutes;
}
public boolean isPercent() {
return isPercent;
}
public void setIsPercent(boolean yesIsPercent) {
this.isPercent = yesIsPercent;
}
public void setStartTime(Long startTime) {
this.start = startTime;
}
public void setEndTime(Long endTime) {
this.end = endTime;
}
@Override
public String toString() {
return "TempBasalPair [" + "Rate=" + insulinRate + ", DurationMinutes=" + durationMinutes + ", IsPercent="
+ isPercent + "]";
}
}

View file

@ -3,8 +3,6 @@ package info.nightscout.androidaps.plugins.pump.common.hw.rileylink;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import android.content.Context;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.pump.common.data.PumpStatus;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RFSpy;
@ -38,7 +36,6 @@ public abstract class RileyLinkCommunicationManager {
private static final int ALLOWED_PUMP_UNREACHABLE = 10 * 60 * 1000; // 10 minutes
protected final RFSpy rfspy;
protected final Context context;
protected int receiverDeviceAwakeForMinutes = 1; // override this in constructor of specific implementation
protected String receiverDeviceID; // String representation of receiver device (ex. Pump (xxxxxx) or Pod (yyyyyy))
protected long lastGoodReceiverCommunicationTime = 0;
@ -51,8 +48,7 @@ public abstract class RileyLinkCommunicationManager {
private int timeoutCount = 0;
public RileyLinkCommunicationManager(Context context, RFSpy rfspy) {
this.context = context;
public RileyLinkCommunicationManager(RFSpy rfspy) {
this.rfspy = rfspy;
this.rileyLinkServiceData = RileyLinkUtil.getRileyLinkServiceData();
RileyLinkUtil.setRileyLinkCommunicationManager(this);
@ -125,7 +121,6 @@ public abstract class RileyLinkCommunicationManager {
}
// FIXME change wakeup
// TODO we might need to fix this. Maybe make pump awake for shorter time (battery factor for pump) - Andy
public void wakeUp(int duration_minutes, boolean force) {
@ -143,8 +138,8 @@ public abstract class RileyLinkCommunicationManager {
LOG.info("Waking pump...");
byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.ReadSimpleData); // simple
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(pumpMsgContent), (byte)0, (byte)200,
(byte)0, (byte)0, 25000, (byte)0);
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(pumpMsgContent), (byte) 0, (byte) 200,
(byte) 0, (byte) 0, 25000, (byte) 0);
if (isLogEnabled())
LOG.info("wakeup: raw response is " + ByteUtil.shortHexString(resp.getRaw()));
@ -225,8 +220,8 @@ public abstract class RileyLinkCommunicationManager {
for (int j = 0; j < tries; j++) {
byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.ReadSimpleData);
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(pumpMsgContent), (byte)0, (byte)0,
(byte)0, (byte)0, 1250, (byte)0);
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(pumpMsgContent), (byte) 0, (byte) 0,
(byte) 0, (byte) 0, 1250, (byte) 0);
if (resp.wasTimeout()) {
LOG.error("scanForPump: Failed to find pump at frequency {}", frequencies[i]);
} else if (resp.looksLikeRadioPacket()) {
@ -258,7 +253,7 @@ public abstract class RileyLinkCommunicationManager {
trial.tries++;
}
sumRSSI += -99.0 * (trial.tries - trial.successes);
trial.averageRSSI2 = (double)(sumRSSI) / (double)(trial.tries);
trial.averageRSSI2 = (double) (sumRSSI) / (double) (trial.tries);
trial.calculateAverage();
@ -273,7 +268,7 @@ public abstract class RileyLinkCommunicationManager {
FrequencyTrial one = results.trials.get(k);
stringBuilder.append(String.format("Scan Result[%s]: Freq=%s, avg RSSI = %s\n", "" + k, ""
+ one.frequencyMHz, "" + one.averageRSSI + ", RSSIs =" + one.rssiList));
+ one.frequencyMHz, "" + one.averageRSSI + ", RSSIs =" + one.rssiList));
}
LOG.info(stringBuilder.toString());
@ -315,7 +310,7 @@ public abstract class RileyLinkCommunicationManager {
// RLMessage msg = makeRLMessage(RLMessageType.ReadSimpleData);
byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.ReadSimpleData);
RadioPacket pkt = new RadioPacket(pumpMsgContent);
RFSpyResponse resp = rfspy.transmitThenReceive(pkt, (byte)0, (byte)0, (byte)0, (byte)0, SCAN_TIMEOUT, (byte)0);
RFSpyResponse resp = rfspy.transmitThenReceive(pkt, (byte) 0, (byte) 0, (byte) 0, (byte) 0, SCAN_TIMEOUT, (byte) 0);
if (resp.wasTimeout()) {
LOG.warn("tune_tryFrequency: no pump response at frequency {}", freqMHz);
} else if (resp.looksLikeRadioPacket()) {
@ -327,8 +322,8 @@ public abstract class RileyLinkCommunicationManager {
LOG.warn("tune_tryFrequency: saw response level {} at frequency {}", radioResponse.rssi, freqMHz);
return calculateRssi(radioResponse.rssi);
} else {
LOG.warn("tune_tryFrequency: invalid radio response:"
+ ByteUtil.shortHexString(radioResponse.getPayload()));
LOG.warn("tune_tryFrequency: invalid radio response:"
+ ByteUtil.shortHexString(radioResponse.getPayload()));
}
} catch (RileyLinkCommunicationException e) {
@ -350,7 +345,7 @@ public abstract class RileyLinkCommunicationManager {
// Try again at larger step size
stepsize += 0.05;
} else {
if ((int)(evenBetterFrequency * 100) == (int)(betterFrequency * 100)) {
if ((int) (evenBetterFrequency * 100) == (int) (betterFrequency * 100)) {
// value did not change, so we're done.
break;
}

View file

@ -8,6 +8,7 @@ import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLin
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice;
import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType;
import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpDeviceState;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodCommandType;
/**
* Created by andy on 5/19/18.
@ -23,10 +24,11 @@ public class RLHistoryItem {
private RileyLinkTargetDevice targetDevice;
private PumpDeviceState pumpDeviceState;
private OmnipodCommandType omnipodCommandType;
public RLHistoryItem(RileyLinkServiceState serviceState, RileyLinkError errorCode,
RileyLinkTargetDevice targetDevice) {
RileyLinkTargetDevice targetDevice) {
this.targetDevice = targetDevice;
this.dateTime = new LocalDateTime();
this.serviceState = serviceState;
@ -50,6 +52,13 @@ public class RLHistoryItem {
}
public RLHistoryItem(OmnipodCommandType omnipodCommandType) {
this.dateTime = new LocalDateTime();
this.omnipodCommandType = omnipodCommandType;
source = RLHistoryItemSource.OmnipodCommand;
}
public LocalDateTime getDateTime() {
return dateTime;
}
@ -71,7 +80,7 @@ public class RLHistoryItem {
switch (this.source) {
case RileyLink:
return "State: " + MainApp.gs(serviceState.getResourceId(targetDevice))
+ (this.errorCode == null ? "" : ", Error Code: " + errorCode);
+ (this.errorCode == null ? "" : ", Error Code: " + errorCode);
case MedtronicPump:
return MainApp.gs(pumpDeviceState.getResourceId());
@ -79,6 +88,9 @@ public class RLHistoryItem {
case MedtronicCommand:
return medtronicCommandType.name();
case OmnipodCommand:
return omnipodCommandType.name();
default:
return "Unknown Description";
}
@ -97,7 +109,8 @@ public class RLHistoryItem {
public enum RLHistoryItemSource {
RileyLink("RileyLink"), //
MedtronicPump("Medtronic"), //
MedtronicCommand("Medtronic");
MedtronicCommand("Medtronic"), //
OmnipodCommand("Omnipod");
private String desc;

View file

@ -33,7 +33,6 @@ import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.db.Source;
import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.events.EventCustomActionsChanged;
import info.nightscout.androidaps.events.EventRefreshOverview;
import info.nightscout.androidaps.interfaces.PluginDescription;
import info.nightscout.androidaps.interfaces.PluginType;
@ -1576,11 +1575,6 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter
this.hasTimeDateOrTimeZoneChanged = true;
}
private void refreshCustomActionsList() {
MainApp.bus().post(new EventCustomActionsChanged());
}
public void setEnableCustomAction(MedtronicCustomActionType customAction, boolean isEnabled) {
if (customAction == MedtronicCustomActionType.ClearBolusBlock) {

View file

@ -1,6 +1,5 @@
package info.nightscout.androidaps.plugins.pump.medtronic.comm;
import android.content.Context;
import android.os.SystemClock;
import org.joda.time.LocalDateTime;
@ -71,8 +70,8 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
private boolean doWakeUpBeforeCommand = true;
public MedtronicCommunicationManager(Context context, RFSpy rfspy) {
super(context, rfspy);
public MedtronicCommunicationManager(RFSpy rfspy) {
super(rfspy);
medtronicCommunicationManager = this;
this.medtronicConverter = new MedtronicConverter();
this.pumpHistoryDecoder = new MedtronicPumpHistoryDecoder();

View file

@ -1,7 +1,5 @@
package info.nightscout.androidaps.plugins.pump.medtronic.data.dto;
import com.google.gson.annotations.Expose;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -18,18 +16,10 @@ import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil;
* <p>
* Just need a class to keep the pair together, for parcel transport.
*/
public class TempBasalPair {
public class TempBasalPair extends info.nightscout.androidaps.plugins.pump.common.data.TempBasalPair {
private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM);
@Expose
private double insulinRate = 0.0d;
@Expose
private int durationMinutes = 0;
@Expose
private boolean isPercent = false;
public TempBasalPair() {
}
@ -42,6 +32,8 @@ public class TempBasalPair {
* @param isPercent
*/
public TempBasalPair(byte rateByte, int startTimeByte, boolean isPercent) {
super();
int rateInt = ByteUtil.asUINT8(rateByte);
if (isPercent)
@ -53,13 +45,6 @@ public class TempBasalPair {
}
public TempBasalPair(double insulinRate, boolean isPercent, int durationMinutes) {
this.insulinRate = insulinRate;
this.isPercent = isPercent;
this.durationMinutes = durationMinutes;
}
public TempBasalPair(byte[] response) {
if (L.isEnabled(L.PUMPCOMM))
@ -86,33 +71,8 @@ public class TempBasalPair {
}
public double getInsulinRate() {
return insulinRate;
}
public void setInsulinRate(double insulinRate) {
this.insulinRate = insulinRate;
}
public int getDurationMinutes() {
return durationMinutes;
}
public void setDurationMinutes(int durationMinutes) {
this.durationMinutes = durationMinutes;
}
public boolean isPercent() {
return isPercent;
}
public void setIsPercent(boolean yesIsPercent) {
this.isPercent = yesIsPercent;
public TempBasalPair(double insulinRate, boolean isPercent, int durationMinutes) {
super(insulinRate, isPercent, durationMinutes);
}
@ -166,10 +126,4 @@ public class TempBasalPair {
}
}
@Override
public String toString() {
return "TempBasalPair [" + "Rate=" + insulinRate + ", DurationMinutes=" + durationMinutes + ", IsPercent="
+ isPercent + "]";
}
}

View file

@ -109,7 +109,7 @@ public class RileyLinkMedtronicService extends RileyLinkService {
RileyLinkUtil.setRileyLinkBLE(rileyLinkBLE);
// init rileyLinkCommunicationManager
medtronicCommunicationManager = new MedtronicCommunicationManager(context, rfspy);
medtronicCommunicationManager = new MedtronicCommunicationManager(rfspy);
}

View file

@ -0,0 +1,514 @@
package info.nightscout.androidaps.plugins.pump.omnipod;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.text.Spanned;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.crashlytics.android.Crashlytics;
import com.joanzapata.iconify.widget.IconTextView;
import com.squareup.otto.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.events.EventExtendedBolusChange;
import info.nightscout.androidaps.events.EventPumpStatusChanged;
import info.nightscout.androidaps.events.EventTempBasalChange;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.common.SubscriberFragment;
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyLinkStatusActivity;
import info.nightscout.androidaps.plugins.pump.omnipod.events.EventOmnipodDeviceStatusChange;
import info.nightscout.androidaps.plugins.pump.omnipod.events.EventOmnipodPumpValuesChanged;
import info.nightscout.androidaps.plugins.pump.omnipod.service.OmnipodPumpStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodUtil;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.queue.events.EventQueueChanged;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.SetWarnColor;
/**
* Created by andy on 4.8.2019
*/
public class OmnipodFragment extends SubscriberFragment {
private static Logger LOG = LoggerFactory.getLogger(L.PUMP);
@BindView(R.id.omnipod_lastconnection)
TextView lastConnectionView;
@BindView(R.id.omnipod_lastbolus)
TextView lastBolusView;
@BindView(R.id.omnipod_basabasalrate)
TextView basaBasalRateView;
@BindView(R.id.omnipod_tempbasal)
TextView tempBasalView;
@BindView(R.id.omnipod_pumpstate_battery)
TextView batteryView;
@BindView(R.id.omnipod_rl_status)
IconTextView rileyLinkStatus;
@BindView(R.id.omnipod_reservoir)
TextView reservoirView;
@BindView(R.id.omnipod_errors)
TextView errorsView;
@BindView(R.id.omnipod_queue)
TextView queueView;
@BindView(R.id.overview_pumpstatuslayout_omnipod)
LinearLayout pumpStatusLayout;
@BindView(R.id.overview_pump_omnipod)
TextView overviewPumpOmnipodView;
@BindView(R.id.omnipod_pod_status)
IconTextView pumpStatusIconView;
@BindView(R.id.omnipod_refresh)
Button refreshButton;
private Handler loopHandler = new Handler();
private static Activity localActivity;
static Button refreshButtonStatic;
private Runnable refreshLoop = new Runnable() {
@Override
public void run() {
updateGUI();
loopHandler.postDelayed(refreshLoop, 60 * 1000L);
}
};
public OmnipodFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
loopHandler.postDelayed(refreshLoop, 60 * 1000L);
}
@Override
public void onDestroy() {
super.onDestroy();
loopHandler.removeCallbacks(refreshLoop);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
try {
View view = inflater.inflate(R.layout.omnipod_fragment, container, false);
unbinder = ButterKnife.bind(this, view);
overviewPumpOmnipodView.setBackgroundColor(MainApp.sResources.getColor(R.color.colorInitializingBorder));
rileyLinkStatus.setText(getTranslation(RileyLinkServiceState.NotStarted.getResourceId(getTargetDevice())));
rileyLinkStatus.setTextSize(14);
pumpStatusIconView.setTextColor(Color.WHITE);
pumpStatusIconView.setTextSize(14);
pumpStatusIconView.setText("{fa-bed}");
refreshButtonStatic = refreshButton;
return view;
} catch (Exception e) {
Crashlytics.logException(e);
}
return null;
}
@OnClick(R.id.omnipod_pod_mgmt)
void onHistoryClick() {
// TODO
// if (MedtronicUtil.getPumpStatus().verifyConfiguration()) {
// startActivity(new Intent(getContext(), MedtronicHistoryActivity.class));
// } else {
// MedtronicUtil.displayNotConfiguredDialog(getContext());
// }
}
@OnClick(R.id.omnipod_refresh)
void onRefreshClick() {
// TODO
// if (!MedtronicUtil.getPumpStatus().verifyConfiguration()) {
// MedtronicUtil.displayNotConfiguredDialog(getContext());
// return;
// }
//
// if (refreshButtonStatic != null)
// refreshButtonStatic.setEnabled(false);
//
// MedtronicPumpPlugin.getPlugin().resetStatusState();
//
// ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("Clicked refresh", new Callback() {
//
// @Override
// public void run() {
// Activity activity = getActivity();
//
// if (activity != null) {
// activity.runOnUiThread(() -> {
// if (refreshButtonStatic != null)
// refreshButtonStatic.setEnabled(true);
// });
// }
// }
// });
}
@OnClick(R.id.omnipod_stats)
void onStatsClick() {
if (OmnipodUtil.getPumpStatus().verifyConfiguration()) {
startActivity(new Intent(getContext(), RileyLinkStatusActivity.class));
} else {
OmnipodUtil.displayNotConfiguredDialog(getContext());
}
}
@Subscribe
public void onStatusEvent(final EventPumpStatusChanged c) {
updateGUI();
}
public static void refreshButtonEnabled(boolean enable) {
if (localActivity != null) {
localActivity.runOnUiThread(() -> {
if (refreshButtonStatic != null) {
refreshButtonStatic.setEnabled(enable);
}
});
}
}
public static Activity getCustomActivity() {
return localActivity;
}
@Subscribe
public void onStatusEvent(final EventOmnipodDeviceStatusChange eventStatusChange) {
if (isLogEnabled())
LOG.info("onStatusEvent(EventOmnipodDeviceStatusChange): {}", eventStatusChange);
Activity activity = getActivity();
if (activity != null) {
localActivity = activity;
activity.runOnUiThread(() -> {
OmnipodPumpStatus pumpStatus = OmnipodUtil.getPumpStatus();
setDeviceStatus(pumpStatus);
});
}
}
private synchronized void setDeviceStatus(OmnipodPumpStatus pumpStatus) {
pumpStatus.rileyLinkServiceState = (RileyLinkServiceState) checkStatusSet(pumpStatus.rileyLinkServiceState,
RileyLinkUtil.getServiceState());
if (pumpStatus.rileyLinkServiceState != null && rileyLinkStatus != null) {
int resourceId = pumpStatus.rileyLinkServiceState.getResourceId(getTargetDevice());
rileyLinkStatus.setTextColor(Color.WHITE);
rileyLinkStatus.setTextSize(14);
if (pumpStatus.rileyLinkServiceState == RileyLinkServiceState.NotStarted) {
rileyLinkStatus.setText(getTranslation(resourceId));
rileyLinkStatus.setTextSize(14);
} else if (pumpStatus.rileyLinkServiceState.isConnecting()) {
rileyLinkStatus.setText("{fa-bluetooth-b spin} " + getTranslation(resourceId));
} else if (pumpStatus.rileyLinkServiceState.isError()) {
RileyLinkError rileyLinkError = RileyLinkUtil.getError();
if (rileyLinkError == null)
rileyLinkStatus.setText("{fa-bluetooth-b} " + getTranslation(resourceId));
else
rileyLinkStatus.setText("{fa-bluetooth-b} "
+ getTranslation(rileyLinkError.getResourceId(RileyLinkTargetDevice.MedtronicPump)));
rileyLinkStatus.setTextColor(Color.RED);
} else {
rileyLinkStatus.setText("{fa-bluetooth-b} " + getTranslation(resourceId));
}
}
pumpStatus.rileyLinkError = (RileyLinkError) checkStatusSet(pumpStatus.rileyLinkError, RileyLinkUtil.getError());
if (errorsView != null) {
if (pumpStatus.rileyLinkError != null) {
int resourceId = pumpStatus.rileyLinkError.getResourceId(getTargetDevice());
errorsView.setText(getTranslation(resourceId));
} else
errorsView.setText("-");
}
// pumpStatus.pumpDeviceState = (PumpDeviceState) checkStatusSet(pumpStatus.pumpDeviceState,
// MedtronicUtil.getPumpDeviceState());
//
if (pumpStatusIconView != null) {
//
// if (pumpStatus.pumpDeviceState != null) {
//
// switch (pumpStatus.pumpDeviceState) {
// case Sleeping:
// pumpStatusIconView.setText("{fa-bed} "); // + pumpStatus.pumpDeviceState.name());
// break;
//
// case NeverContacted:
// case WakingUp:
// case PumpUnreachable:
// case ErrorWhenCommunicating:
// case TimeoutWhenCommunicating:
// case InvalidConfiguration:
// pumpStatusIconView.setText(" " + getTranslation(pumpStatus.pumpDeviceState.getResourceId()));
// break;
//
// case Active: {
// MedtronicCommandType cmd = MedtronicUtil.getCurrentCommand();
//
// LOG.debug("Command: " + cmd);
//
// if (cmd == null)
// pumpStatusIconView.setText(" " + MainApp.gs(pumpStatus.pumpDeviceState.getResourceId()));
// else {
// Integer resourceId = cmd.getResourceId();
//
// if (cmd == MedtronicCommandType.GetHistoryData) {
//
// if (MedtronicUtil.frameNumber == null) {
// pumpStatusIconView.setText(MainApp.gs(
// R.string.medtronic_cmd_desc_get_history_request, MedtronicUtil.pageNumber));
// } else {
// pumpStatusIconView.setText(MainApp.gs(resourceId, MedtronicUtil.pageNumber,
// MedtronicUtil.frameNumber));
// }
//
// } else {
// if (resourceId == null) {
// pumpStatusIconView.setText(" " + cmd.getCommandDescription());
// } else {
// pumpStatusIconView.setText(" " + getTranslation(resourceId));
// }
// }
//
// }
//
// }
// break;
//
// default:
// LOG.warn("Unknown pump state: " + pumpStatus.pumpDeviceState);
// }
// } else {
// pumpStatusIconView.setText("{fa-bed} ");
// }
// TODO
pumpStatusIconView.setText("{fa-bed} ");
}
if (queueView != null) {
Spanned status = ConfigBuilderPlugin.getPlugin().getCommandQueue().spannedStatus();
if (status.toString().equals("")) {
queueView.setVisibility(View.GONE);
} else {
queueView.setVisibility(View.VISIBLE);
queueView.setText(status);
}
}
}
public Object checkStatusSet(Object object1, Object object2) {
if (object1 == null) {
return object2;
} else {
if (!object1.equals(object2)) {
return object2;
} else
return object1;
}
}
public RileyLinkTargetDevice getTargetDevice() {
return RileyLinkTargetDevice.Omnipod;
}
public String getTranslation(int resourceId) {
return MainApp.gs(resourceId);
}
@Subscribe
public void onStatusEvent(final EventOmnipodPumpValuesChanged s) {
if (isLogEnabled())
LOG.debug("EventOmnipodPumpValuesChanged triggered");
updateGUI();
}
@Subscribe
public void onStatusEvent(final EventTempBasalChange s) {
updateGUI();
}
@Subscribe
public void onStatusEvent(final EventExtendedBolusChange s) {
updateGUI();
}
@Subscribe
public void onStatusEvent(final EventQueueChanged s) {
updateGUI();
}
// GUI functions
@Override
protected void updateGUI() {
Activity activity = getActivity();
if (activity != null && basaBasalRateView != null)
activity.runOnUiThread(() -> {
if (lastConnectionView == null) // ui not yet initialized
return;
localActivity = activity;
OmnipodPumpPlugin plugin = OmnipodPumpPlugin.getPlugin();
OmnipodPumpStatus pumpStatus = OmnipodUtil.getPumpStatus();
setDeviceStatus(pumpStatus);
// last connection
String minAgo = DateUtil.minAgo(pumpStatus.lastConnection);
long min = (System.currentTimeMillis() - pumpStatus.lastConnection) / 1000 / 60;
if (pumpStatus.lastConnection + 60 * 1000 > System.currentTimeMillis()) {
lastConnectionView.setText(R.string.combo_pump_connected_now);
lastConnectionView.setTextColor(Color.WHITE);
} else if (pumpStatus.lastConnection + 30 * 60 * 1000 < System.currentTimeMillis()) {
if (min < 60) {
lastConnectionView.setText(MainApp.gs(R.string.minago, min));
} else if (min < 1440) {
int h = (int) (min / 60);
lastConnectionView.setText(MainApp.gq(R.plurals.objective_hours, h, h) + " "
+ MainApp.gs(R.string.ago));
} else {
int h = (int) (min / 60);
int d = h / 24;
// h = h - (d * 24);
lastConnectionView.setText(MainApp.gq(R.plurals.objective_days, d, d) + " "
+ MainApp.gs(R.string.ago));
}
lastConnectionView.setTextColor(Color.RED);
} else {
lastConnectionView.setText(minAgo);
lastConnectionView.setTextColor(Color.WHITE);
}
// last bolus
Double bolus = pumpStatus.lastBolusAmount;
Date bolusTime = pumpStatus.lastBolusTime;
if (bolus != null && bolusTime != null) {
long agoMsc = System.currentTimeMillis() - pumpStatus.lastBolusTime.getTime();
double bolusMinAgo = agoMsc / 60d / 1000d;
String unit = MainApp.gs(R.string.insulin_unit_shortname);
String ago;
if ((agoMsc < 60 * 1000)) {
ago = MainApp.gs(R.string.combo_pump_connected_now);
} else if (bolusMinAgo < 60) {
ago = DateUtil.minAgo(pumpStatus.lastBolusTime.getTime());
} else {
ago = DateUtil.hourAgo(pumpStatus.lastBolusTime.getTime());
}
lastBolusView.setText(MainApp.gs(R.string.combo_last_bolus, bolus, unit, ago));
} else {
lastBolusView.setText("");
}
// base basal rate
basaBasalRateView.setText("(" + (pumpStatus.activeProfileName) + ") "
+ MainApp.gs(R.string.pump_basebasalrate, plugin.getBaseBasalRate()));
if (ConfigBuilderPlugin.getPlugin().getActivePump().isFakingTempsByExtendedBoluses()) {
if (TreatmentsPlugin.getPlugin().isInHistoryRealTempBasalInProgress()) {
tempBasalView.setText(TreatmentsPlugin.getPlugin()
.getRealTempBasalFromHistory(System.currentTimeMillis()).toStringFull());
} else {
tempBasalView.setText("");
}
} else {
// v2 plugin
if (TreatmentsPlugin.getPlugin().isTempBasalInProgress()) {
tempBasalView.setText(TreatmentsPlugin.getPlugin()
.getTempBasalFromHistory(System.currentTimeMillis()).toStringFull());
} else {
tempBasalView.setText("");
}
}
// battery
// TODO this might need to be removed, I am not sure if pods have battery status
batteryView.setText("{fa-battery-" + (pumpStatus.batteryRemaining / 25) + "} ");
// if (MedtronicUtil.getBatteryType() == BatteryType.None || pumpStatus.batteryVoltage == null) {
// batteryView.setText("{fa-battery-" + (pumpStatus.batteryRemaining / 25) + "} ");
// } else {
// batteryView.setText("{fa-battery-" + (pumpStatus.batteryRemaining / 25) + "} " + pumpStatus.batteryRemaining + "%" + String.format(" (%.2f V)", pumpStatus.batteryVoltage));
// }
SetWarnColor.setColorInverse(batteryView, pumpStatus.batteryRemaining, 25d, 10d);
// reservoir
reservoirView.setText(DecimalFormatter.to0Decimal(pumpStatus.reservoirRemainingUnits) + " / "
+ pumpStatus.reservoirFullUnits + " " + MainApp.gs(R.string.insulin_unit_shortname));
SetWarnColor.setColorInverse(reservoirView, pumpStatus.reservoirRemainingUnits, 50d, 20d);
errorsView.setText(pumpStatus.getErrorInfo());
});
}
private boolean isLogEnabled() {
return L.isEnabled(L.PUMP);
}
}

View file

@ -0,0 +1,848 @@
package info.nightscout.androidaps.plugins.pump.omnipod;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.SystemClock;
import androidx.annotation.NonNull;
import org.joda.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import info.nightscout.androidaps.BuildConfig;
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.Source;
import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.events.EventRefreshOverview;
import info.nightscout.androidaps.interfaces.PluginDescription;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction;
import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType;
import info.nightscout.androidaps.plugins.pump.common.PumpPluginAbstract;
import info.nightscout.androidaps.plugins.pump.common.data.TempBasalPair;
import info.nightscout.androidaps.plugins.pump.common.defs.PumpDriverState;
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ResetRileyLinkConfigurationTask;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTaskExecutor;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.OmnipodCommunicationManager;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.ui.OmnipodUIComm;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.ui.OmnipodUITask;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodCommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodCustomActionType;
import info.nightscout.androidaps.plugins.pump.omnipod.events.EventOmnipodPumpValuesChanged;
import info.nightscout.androidaps.plugins.pump.omnipod.service.OmnipodPumpStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.service.RileyLinkOmnipodService;
import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodConst;
import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodUtil;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.utils.SP;
/**
* Created by andy on 23.04.18.
*
* @author Andy Rozman (andy.rozman@gmail.com)
*/
public class OmnipodPumpPlugin extends PumpPluginAbstract implements PumpInterface {
private static final Logger LOG = LoggerFactory.getLogger(L.PUMP);
protected static OmnipodPumpPlugin plugin = null;
private RileyLinkOmnipodService omnipodService;
private OmnipodPumpStatus pumpStatusLocal = null;
private OmnipodUIComm omnipodUIComm = new OmnipodUIComm();
// variables for handling statuses and history
private boolean firstRun = true;
private boolean isRefresh = false;
private boolean isBasalProfileInvalid = false;
private boolean basalProfileChanged = false;
private boolean isInitialized = false;
private OmnipodCommunicationManager omnipodCommunicationManager;
public static boolean isBusy = false;
private List<Long> busyTimestamps = new ArrayList<>();
private boolean sentIdToFirebase = false;
private boolean hasTimeDateOrTimeZoneChanged = false;
private Profile currentProfile;
private OmnipodPumpPlugin() {
super(new PluginDescription() //
.mainType(PluginType.PUMP) //
.fragmentClass(OmnipodFragment.class.getName()) //
.pluginName(R.string.omnipod_name) //
.shortName(R.string.omnipod_name_short) //
.preferencesId(R.xml.pref_omnipod) //
.description(R.string.description_pump_omnipod), //
PumpType.Insulet_Omnipod
);
displayConnectionMessages = false;
serviceConnection = new ServiceConnection() {
public void onServiceDisconnected(ComponentName name) {
if (isLoggingEnabled())
LOG.debug("RileyLinkOmnipodService is disconnected");
omnipodService = null;
}
public void onServiceConnected(ComponentName name, IBinder service) {
if (isLoggingEnabled())
LOG.debug("RileyLinkOmnipodService is connected");
RileyLinkOmnipodService.LocalBinder mLocalBinder = (RileyLinkOmnipodService.LocalBinder) service;
omnipodService = mLocalBinder.getServiceInstance();
new Thread(() -> {
for (int i = 0; i < 20; i++) {
SystemClock.sleep(5000);
if (OmnipodUtil.getPumpStatus() != null) {
if (isLoggingEnabled())
LOG.debug("Starting OmniPod-RileyLink service");
if (OmnipodUtil.getPumpStatus().setNotInPreInit()) {
break;
}
}
}
}).start();
}
};
}
public static OmnipodPumpPlugin getPlugin() {
if (plugin == null)
plugin = new OmnipodPumpPlugin();
return plugin;
}
private String getLogPrefix() {
return "OmnipodPlugin::";
}
@Override
public void initPumpStatusData() {
this.pumpStatusLocal = new OmnipodPumpStatus(pumpDescription);
OmnipodUtil.setPumpStatus(pumpStatusLocal);
pumpStatusLocal.lastConnection = SP.getLong(RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L);
pumpStatusLocal.lastDataTime = new LocalDateTime(pumpStatusLocal.lastConnection);
pumpStatusLocal.previousConnection = pumpStatusLocal.lastConnection;
pumpStatusLocal.refreshConfiguration();
if (isLoggingEnabled())
LOG.debug("initPumpStatusData: {}", this.pumpStatusLocal);
this.pumpStatus = pumpStatusLocal;
// set first Omnipod Start
if (!SP.contains(OmnipodConst.Statistics.FirstPumpStart)) {
SP.putLong(OmnipodConst.Statistics.FirstPumpStart, System.currentTimeMillis());
}
}
public void onStartCustomActions() {
// check status every minute (if any status needs refresh we send readStatus command)
new Thread(() -> {
do {
SystemClock.sleep(60000);
if (this.isInitialized) {
clearBusyQueue();
}
} while (serviceRunning);
}).start();
}
public Class getServiceClass() {
return RileyLinkOmnipodService.class;
}
@Override
public String deviceID() {
return "Omnipod";
}
// Pump Plugin
private boolean isServiceSet() {
return omnipodService != null;
}
@Override
public boolean isInitialized() {
if (isLoggingEnabled() && displayConnectionMessages)
LOG.debug(getLogPrefix() + "isInitialized");
return isServiceSet() && isInitialized;
}
@Override
public boolean isBusy() {
if (isLoggingEnabled() && displayConnectionMessages)
LOG.debug(getLogPrefix() + "isBusy");
if (isServiceSet()) {
if (isBusy)
return true;
if (busyTimestamps.size() > 0) {
clearBusyQueue();
if (busyTimestamps.size() > 0) {
return true;
}
}
}
return false;
}
private synchronized void clearBusyQueue() {
if (busyTimestamps.size() == 0) {
return;
}
Set<Long> deleteFromQueue = new HashSet<>();
for (Long busyTimestamp : busyTimestamps) {
if (System.currentTimeMillis() > busyTimestamp) {
deleteFromQueue.add(busyTimestamp);
}
}
if (deleteFromQueue.size() == busyTimestamps.size()) {
busyTimestamps.clear();
//setEnableCustomAction(MedtronicCustomActionType.ClearBolusBlock, false);
}
if (deleteFromQueue.size() > 0) {
busyTimestamps.removeAll(deleteFromQueue);
}
}
@Override
public boolean isConnected() {
if (isLoggingEnabled() && displayConnectionMessages)
LOG.debug(getLogPrefix() + "isConnected");
return isServiceSet() && omnipodService.isInitialized();
}
@Override
public boolean isConnecting() {
if (isLoggingEnabled() && displayConnectionMessages)
LOG.debug(getLogPrefix() + "isConnecting");
return !isServiceSet() || !omnipodService.isInitialized();
}
@Override
public void getPumpStatus() {
if (firstRun) {
initializePump(!isRefresh);
}
// getPodPumpStatus();
//
// if (firstRun) {
// initializePump(!isRefresh);
// } else {
// refreshAnyStatusThatNeedsToBeRefreshed();
// }
//
// MainApp.bus().post(new EventMedtronicPumpValuesChanged());
}
void resetStatusState() {
firstRun = true;
isRefresh = true;
}
private void setRefreshButtonEnabled(boolean enabled) {
OmnipodFragment.refreshButtonEnabled(enabled);
}
private void initializePump(boolean realInit) {
if (isLoggingEnabled())
LOG.info(getLogPrefix() + "initializePump - start");
if (omnipodCommunicationManager == null) {
omnipodCommunicationManager = OmnipodCommunicationManager.getInstance();
}
// setRefreshButtonEnabled(false);
//
// getPodPumpStatus();
//
// if (isRefresh) {
// if (isPumpNotReachable()) {
// if (isLoggingEnabled())
// LOG.error(getLogPrefix() + "initializePump::Pump unreachable.");
// MedtronicUtil.sendNotification(MedtronicNotificationType.PumpUnreachable);
//
// setRefreshButtonEnabled(true);
//
// return;
// }
//
// MedtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable);
// }
//
// this.pumpState = PumpDriverState.Connected;
//
// pumpStatusLocal.setLastCommunicationToNow();
// setRefreshButtonEnabled(true);
// TODO need to read status and BasalProfile if pod inited and pod status and set correct commands enabled
if (!isRefresh) {
pumpState = PumpDriverState.Initialized;
}
if (!sentIdToFirebase) {
Bundle params = new Bundle();
params.putString("version", BuildConfig.VERSION);
MainApp.getFirebaseAnalytics().logEvent("OmnipodPumpInit", params);
sentIdToFirebase = true;
}
isInitialized = true;
// this.pumpState = PumpDriverState.Initialized;
this.firstRun = false;
}
@Override
public boolean isThisProfileSet(Profile profile) {
// status was not yet read from pod
if (currentProfile == null) {
return true;
}
return (currentProfile.isProfileTheSame(profile));
}
@Override
public long lastDataTime() {
getPodPumpStatusObject();
if (pumpStatusLocal.lastConnection != 0) {
return pumpStatusLocal.lastConnection;
}
return System.currentTimeMillis();
}
@Override
public double getBaseBasalRate() {
if (currentProfile != null) {
int hour = (new GregorianCalendar()).get(Calendar.HOUR_OF_DAY);
return currentProfile.getBasalTimeFromMidnight(getTimeInS(hour * 60));
} else {
return 0.0d;
}
}
@Override
public double getReservoirLevel() {
// TODO
return getPodPumpStatusObject().reservoirRemainingUnits;
}
@Override
public int getBatteryLevel() {
// TODO
return getPodPumpStatusObject().batteryRemaining;
}
private OmnipodPumpStatus getPodPumpStatusObject() {
if (pumpStatusLocal == null) {
// FIXME I don't know why this happens
if (isLoggingEnabled())
LOG.warn("!!!! Reset Pump Status Local");
pumpStatusLocal = OmnipodUtil.getPumpStatus();
}
return pumpStatusLocal;
}
protected void triggerUIChange() {
MainApp.bus().post(new EventOmnipodPumpValuesChanged());
}
@NonNull
protected PumpEnactResult deliverBolus(final DetailedBolusInfo detailedBolusInfo) {
LOG.info(getLogPrefix() + "deliverBolus - {}", detailedBolusInfo);
setRefreshButtonEnabled(false);
try {
OmnipodUITask responseTask = omnipodUIComm.executeCommand(OmnipodCommandType.SetBolus,
detailedBolusInfo.insulin);
Boolean response = (Boolean) responseTask.returnData;
setRefreshButtonEnabled(true);
if (response) {
TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, true);
// we subtract insulin, exact amount will be visible with next remainingInsulin update.
getPodPumpStatusObject().reservoirRemainingUnits -= detailedBolusInfo.insulin;
incrementStatistics(detailedBolusInfo.isSMB ? OmnipodConst.Statistics.SMBBoluses
: OmnipodConst.Statistics.StandardBoluses);
// calculate time for bolus and set driver to busy for that time
// TODO fix this
int bolusTime = (int) (detailedBolusInfo.insulin * 42.0d);
long time = System.currentTimeMillis() + (bolusTime * 1000);
this.busyTimestamps.add(time);
return new PumpEnactResult().success(true) //
.enacted(true) //
.bolusDelivered(detailedBolusInfo.insulin) //
.carbsDelivered(detailedBolusInfo.carbs);
} else {
return new PumpEnactResult() //
.success(false) //
.enacted(false) //
.comment(MainApp.gs(R.string.medtronic_cmd_bolus_could_not_be_delivered));
}
} finally {
finishAction("Bolus");
}
}
private PumpEnactResult setNotReachable(boolean isBolus, boolean success) {
setRefreshButtonEnabled(true);
if (success) {
return new PumpEnactResult() //
.success(true) //
.enacted(false);
} else {
return new PumpEnactResult() //
.success(false) //
.enacted(false) //
.comment(MainApp.gs(R.string.medtronic_pump_status_pump_unreachable));
}
}
public void stopBolusDelivering() {
LOG.info(getLogPrefix() + "stopBolusDelivering");
setRefreshButtonEnabled(false);
try {
OmnipodUITask responseTask = omnipodUIComm.executeCommand(OmnipodCommandType.CancelBolus);
Boolean response = (Boolean) responseTask.returnData;
setRefreshButtonEnabled(true);
LOG.info(getLogPrefix() + "stopBolusDelivering - wasSuccess={}", response);
if (response) {
// TODO fix bolus record with cancel
//TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, true);
}
} finally {
finishAction("Bolus");
}
}
private void incrementStatistics(String statsKey) {
long currentCount = SP.getLong(statsKey, 0L);
currentCount++;
SP.putLong(statsKey, currentCount);
}
// if enforceNew===true current temp basal is canceled and new TBR set (duration is prolonged),
// if false and the same rate is requested enacted=false and success=true is returned and TBR is not changed
@Override
public PumpEnactResult setTempBasalAbsolute(Double absoluteRate, Integer durationInMinutes, Profile profile,
boolean enforceNew) {
getPodPumpStatusObject();
setRefreshButtonEnabled(false);
if (isLoggingEnabled())
LOG.info(getLogPrefix() + "setTempBasalAbsolute: rate: {}, duration={}", absoluteRate, durationInMinutes);
// read current TBR
TempBasalPair tbrCurrent = readTBR();
if (tbrCurrent != null) {
if (isLoggingEnabled())
LOG.info(getLogPrefix() + "setTempBasalAbsolute: Current Basal: duration: {} min, rate={}",
tbrCurrent.getDurationMinutes(), tbrCurrent.getInsulinRate());
}
if (tbrCurrent != null && !enforceNew) {
if (OmnipodUtil.isSame(tbrCurrent.getInsulinRate(), absoluteRate)) {
if (isLoggingEnabled())
LOG.info(getLogPrefix() + "setTempBasalAbsolute - No enforceNew and same rate. Exiting.");
finishAction("TBR");
return new PumpEnactResult().success(true).enacted(false);
}
}
// if TBR is running we will cancel it.
if (tbrCurrent != null) {
if (isLoggingEnabled())
LOG.info(getLogPrefix() + "setTempBasalAbsolute - TBR running - so canceling it.");
// CANCEL
OmnipodUITask responseTask2 = omnipodUIComm.executeCommand(OmnipodCommandType.CancelTemporaryBasal);
Boolean response = (Boolean) responseTask2.returnData;
if (response) {
if (isLoggingEnabled())
LOG.info(getLogPrefix() + "setTempBasalAbsolute - Current TBR cancelled.");
} else {
if (isLoggingEnabled())
LOG.error(getLogPrefix() + "setTempBasalAbsolute - Cancel TBR failed.");
finishAction("TBR");
return new PumpEnactResult().success(false).enacted(false)
.comment(MainApp.gs(R.string.medtronic_cmd_cant_cancel_tbr_stop_op));
}
}
// now start new TBR
OmnipodUITask responseTask = omnipodUIComm.executeCommand(OmnipodCommandType.SetTemporaryBasal,
absoluteRate, durationInMinutes);
Boolean response = (Boolean) responseTask.returnData;
if (isLoggingEnabled())
LOG.info(getLogPrefix() + "setTempBasalAbsolute - setTBR. Response: " + response);
if (response) {
pumpStatusLocal.tempBasalStart = System.currentTimeMillis();
pumpStatusLocal.tempBasalAmount = absoluteRate;
pumpStatusLocal.tempBasalLength = durationInMinutes;
pumpStatusLocal.tempBasalEnd = getTimeInFutureFromMinutes(durationInMinutes);
TemporaryBasal tempStart = new TemporaryBasal() //
.date(System.currentTimeMillis()) //
.duration(durationInMinutes) //
.absolute(absoluteRate) //
.source(Source.USER);
TreatmentsPlugin.getPlugin().addToHistoryTempBasal(tempStart);
incrementStatistics(OmnipodConst.Statistics.TBRsSet);
finishAction("TBR");
return new PumpEnactResult().success(true).enacted(true) //
.absolute(absoluteRate).duration(durationInMinutes);
} else {
finishAction("TBR");
return new PumpEnactResult().success(false).enacted(false) //
.comment(MainApp.gs(R.string.medtronic_cmd_tbr_could_not_be_delivered));
}
}
private TempBasalPair readTBR() {
// TODO we can do it like this or read status from pod ??
if (pumpStatusLocal.tempBasalEnd < System.currentTimeMillis()) {
// TBR done
pumpStatusLocal.clearTemporaryBasal();
return null;
}
return pumpStatusLocal.getTemporaryBasal();
}
private void finishAction(String overviewKey) {
if (overviewKey != null)
MainApp.bus().post(new EventRefreshOverview(overviewKey));
triggerUIChange();
setRefreshButtonEnabled(true);
}
private long getTimeInFutureFromMinutes(int minutes) {
return System.currentTimeMillis() + getTimeInMs(minutes);
}
private long getTimeInMs(int minutes) {
return getTimeInS(minutes) * 1000L;
}
private int getTimeInS(int minutes) {
return minutes * 60;
}
@Override
public PumpEnactResult cancelTempBasal(boolean enforceNew) {
if (isLoggingEnabled())
LOG.info(getLogPrefix() + "cancelTempBasal - started");
setRefreshButtonEnabled(false);
TempBasalPair tbrCurrent = readTBR();
if (tbrCurrent == null) {
if (isLoggingEnabled())
LOG.info(getLogPrefix() + "cancelTempBasal - TBR already canceled.");
finishAction("TBR");
return new PumpEnactResult().success(true).enacted(false);
}
OmnipodUITask responseTask2 = omnipodUIComm.executeCommand(OmnipodCommandType.CancelTemporaryBasal);
Boolean response = (Boolean) responseTask2.returnData;
finishAction("TBR");
if (response) {
if (isLoggingEnabled())
LOG.info(getLogPrefix() + "cancelTempBasal - Cancel TBR successful.");
TemporaryBasal tempBasal = new TemporaryBasal() //
.date(System.currentTimeMillis()) //
.duration(0) //
.source(Source.USER);
TreatmentsPlugin.getPlugin().addToHistoryTempBasal(tempBasal);
return new PumpEnactResult().success(true).enacted(true) //
.isTempCancel(true);
} else {
if (isLoggingEnabled())
LOG.info(getLogPrefix() + "cancelTempBasal - Cancel TBR failed.");
return new PumpEnactResult().success(response).enacted(response) //
.comment(MainApp.gs(R.string.medtronic_cmd_cant_cancel_tbr));
}
}
@Override
public String serialNumber() {
return getPodPumpStatusObject().podNumber;
}
@Override
public PumpEnactResult setNewBasalProfile(Profile profile) {
if (isLoggingEnabled())
LOG.info(getLogPrefix() + "setNewBasalProfile");
// this shouldn't be needed, but let's do check if profile setting we are setting is same as current one
if (this.currentProfile != null && this.currentProfile.isProfileTheSame(profile)) {
return new PumpEnactResult() //
.success(true) //
.enacted(false) //
.comment(MainApp.gs(R.string.medtronic_cmd_basal_profile_not_set_is_same));
}
setRefreshButtonEnabled(false);
OmnipodUITask responseTask = omnipodUIComm.executeCommand(OmnipodCommandType.SetBasalProfile,
profile);
Boolean response = (Boolean) responseTask.returnData;
if (isLoggingEnabled())
LOG.info(getLogPrefix() + "Basal Profile was set: " + response);
if (response) {
this.currentProfile = profile;
return new PumpEnactResult().success(true).enacted(true);
} else {
return new PumpEnactResult().success(response).enacted(response) //
.comment(MainApp.gs(R.string.medtronic_cmd_basal_profile_could_not_be_set));
}
}
// OPERATIONS not supported by Pump or Plugin
private List<CustomAction> customActions = null;
private CustomAction customActionResetRLConfig = new CustomAction(
R.string.medtronic_custom_action_reset_rileylink, OmnipodCustomActionType.ResetRileyLinkConfiguration, true);
private CustomAction customActionInitPod = new CustomAction(
R.string.omnipod_cmd_init_pod, OmnipodCustomActionType.InitPod, true);
private CustomAction customActionDeactivatePod = new CustomAction(
R.string.omnipod_cmd_deactivate_pod, OmnipodCustomActionType.DeactivatePod, false);
private CustomAction customActionResetPod = new CustomAction(
R.string.omnipod_cmd_reset_pod, OmnipodCustomActionType.ResetPodStatus, true);
@Override
public List<CustomAction> getCustomActions() {
if (customActions == null) {
this.customActions = Arrays.asList(
customActionResetRLConfig);
}
return this.customActions;
}
// TODO we need to brainstorm how we want to do this -- Andy
@Override
public void executeCustomAction(CustomActionType customActionType) {
OmnipodCustomActionType mcat = (OmnipodCustomActionType) customActionType;
switch (mcat) {
case ResetRileyLinkConfiguration: {
ServiceTaskExecutor.startTask(new ResetRileyLinkConfigurationTask());
}
break;
case InitPod: {
omnipodUIComm.executeCommand(OmnipodCommandType.InitPod);
}
break;
case DeactivatePod: {
omnipodUIComm.executeCommand(OmnipodCommandType.DeactivatePod);
}
break;
case ResetPodStatus: {
omnipodUIComm.executeCommand(OmnipodCommandType.ResetPodStatus);
}
break;
default:
break;
}
}
@Override
public void timeDateOrTimeZoneChanged() {
// if (isLoggingEnabled())
// LOG.warn(getLogPrefix() + "Time, Date and/or TimeZone changed. ");
//
// this.hasTimeDateOrTimeZoneChanged = true;
}
public void setEnableCustomAction(OmnipodCustomActionType customAction, boolean isEnabled) {
switch (customAction) {
case InitPod: {
this.customActionInitPod.setEnabled(isEnabled);
}
break;
case DeactivatePod: {
this.customActionDeactivatePod.setEnabled(isEnabled);
}
break;
default:
break;
}
refreshCustomActionsList();
}
}

View file

@ -0,0 +1,122 @@
package info.nightscout.androidaps.plugins.pump.omnipod.comm;
import android.content.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.pump.common.data.TempBasalPair;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkCommunicationManager;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RFSpy;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RLMessage;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RLMessageType;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.data.PodCommResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodUtil;
import info.nightscout.androidaps.utils.SP;
/**
* Created by andy on 4.8.2019
*/
public class OmnipodCommunicationManager extends RileyLinkCommunicationManager implements OmnipodCommunicationManagerInterface {
private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM);
private static OmnipodCommunicationManager omnipodCommunicationManager;
String errorMessage;
public OmnipodCommunicationManager(Context context, RFSpy rfspy) {
super(rfspy);
omnipodCommunicationManager = this;
OmnipodUtil.getPumpStatus().previousConnection = SP.getLong(
RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L);
}
public static OmnipodCommunicationManager getInstance() {
return omnipodCommunicationManager;
}
@Override
protected void configurePumpSpecificSettings() {
pumpStatus = OmnipodUtil.getPumpStatus();
}
@Override
public <E extends RLMessage> E createResponseMessage(byte[] payload, Class<E> clazz) {
// TODO
//PumpMessage pumpMessage = new PumpMessage(payload);
//eturn (E) pumpMessage;
return null;
}
@Override
public boolean tryToConnectToDevice() {
return false; //isDeviceReachable(true);
}
public String getErrorResponse() {
return this.errorMessage;
}
@Override
public byte[] createPumpMessageContent(RLMessageType type) {
return new byte[0];
}
private boolean isLogEnabled() {
return L.isEnabled(L.PUMPCOMM);
}
// This are just skeleton methods, we need to see what we can get returned and act accordingly
public PodCommResponse initPod() {
return null;
}
public PodCommResponse getPodStatus() {
return null;
}
public PodCommResponse deactivatePod() {
return null;
}
public PodCommResponse setBasalProfile(Profile profile) {
return null;
}
public PodCommResponse resetPodStatus() {
return null;
}
public PodCommResponse setBolus(Double parameter) {
return null;
}
public PodCommResponse cancelBolus() {
return null;
}
public PodCommResponse setTemporaryBasal(TempBasalPair tbr) {
return null;
}
public PodCommResponse cancelTemporaryBasal() {
return null;
}
}

View file

@ -0,0 +1,61 @@
package info.nightscout.androidaps.plugins.pump.omnipod.comm;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.plugins.pump.common.data.TempBasalPair;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.data.PodCommResponse;
public interface OmnipodCommunicationManagerInterface {
// TODO add methods that can be used by OmniPod Eros and Omnipod Dash
/**
* Initialize Pod
*/
PodCommResponse initPod();
/**
* Get Pod Status (is pod running, battery left ?, reservoir, etc)
*/
PodCommResponse getPodStatus();
/**
* Deactivate Pod
*/
PodCommResponse deactivatePod();
/**
* Set Basal Profile
*/
PodCommResponse setBasalProfile(Profile profile);
/**
* Reset Pod status (if we forget to disconnect Pod and want to init new pod, and want to forget current pod)
*/
PodCommResponse resetPodStatus();
/**
* Set Bolus
*
* @param amount amount of bolus in U
*/
PodCommResponse setBolus(Double amount);
/**
* Cancel Bolus (if bolus is already stopped, return acknowledgment)
*/
PodCommResponse cancelBolus();
/**
* Set Temporary Basal
*
* @param tbr TempBasalPair object containg amount and duration in minutes
*/
PodCommResponse setTemporaryBasal(TempBasalPair tbr);
/**
* Cancel Temporary Basal (if TB is already stopped, return acknowledgment)
*/
PodCommResponse cancelTemporaryBasal();
}

View file

@ -0,0 +1,15 @@
package info.nightscout.androidaps.plugins.pump.omnipod.comm.data;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.PodResponseType;
public class PodCommResponse {
PodResponseType podResponseType;
Boolean acknowledged;
Object customData;
Object errorResponse;
// some status data if it can be returned (battery, reservoir, etc)
}

View file

@ -0,0 +1,83 @@
package info.nightscout.androidaps.plugins.pump.omnipod.comm.ui;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.OmnipodCommunicationManager;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodCommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodUtil;
/**
* Created by andy on 4.8.2019
*/
public class OmnipodUIComm {
private static final Logger LOG = LoggerFactory.getLogger(L.PUMP);
OmnipodCommunicationManager ocmInstance = null;
OmnipodUIPostprocessor uiPostprocessor = new OmnipodUIPostprocessor();
private OmnipodCommunicationManager getCommunicationManager() {
if (ocmInstance == null) {
ocmInstance = OmnipodCommunicationManager.getInstance();
}
return ocmInstance;
}
public synchronized OmnipodUITask executeCommand(OmnipodCommandType commandType, Object... parameters) {
if (isLogEnabled())
LOG.warn("Execute Command: " + commandType.name());
OmnipodUITask task = new OmnipodUITask(commandType, parameters);
OmnipodUtil.setCurrentCommand(commandType);
// new Thread(() -> {
// LOG.warn("@@@ Start Thread");
//
// task.execute(getCommunicationManager());
//
// LOG.warn("@@@ End Thread");
// });
task.execute(getCommunicationManager());
// for (int i = 0; i < getMaxWaitTime(commandType); i++) {
// synchronized (task) {
// // try {
// //
// // //task.wait(1000);
// // } catch (InterruptedException e) {
// // LOG.error("executeCommand InterruptedException", e);
// // }
//
//
// SystemClock.sleep(1000);
// }
//
// if (task.isReceived()) {
// break;
// }
// }
if (!task.isReceived() && isLogEnabled()) {
LOG.warn("Reply not received for " + commandType);
}
task.postProcess(uiPostprocessor);
return task;
}
private boolean isLogEnabled() {
return L.isEnabled(L.PUMP);
}
}

View file

@ -0,0 +1,63 @@
package info.nightscout.androidaps.plugins.pump.omnipod.comm.ui;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.pump.omnipod.OmnipodPumpPlugin;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodCustomActionType;
import info.nightscout.androidaps.plugins.pump.omnipod.service.OmnipodPumpStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodUtil;
/**
* Created by andy on 4.8.2019
*/
public class OmnipodUIPostprocessor {
private static final Logger LOG = LoggerFactory.getLogger(L.PUMP);
OmnipodPumpStatus pumpStatus;
OmnipodPumpPlugin omnipodPumpPlugin;
public OmnipodUIPostprocessor() {
pumpStatus = OmnipodUtil.getPumpStatus();
omnipodPumpPlugin = OmnipodPumpPlugin.getPlugin();
}
// this is mostly intended for command that return certain statuses (Remaining Insulin, ...), and
// where responses won't be directly used
public void postProcessData(OmnipodUITask uiTask) {
switch (uiTask.commandType) {
case InitPod: {
omnipodPumpPlugin.setEnableCustomAction(OmnipodCustomActionType.InitPod, false);
omnipodPumpPlugin.setEnableCustomAction(OmnipodCustomActionType.DeactivatePod, true);
}
break;
case DeactivatePod:
case ResetPodStatus: {
omnipodPumpPlugin.setEnableCustomAction(OmnipodCustomActionType.InitPod, true);
omnipodPumpPlugin.setEnableCustomAction(OmnipodCustomActionType.DeactivatePod, false);
}
break;
default:
if (isLogEnabled())
LOG.trace("Post-processing not implemented for {}.", uiTask.commandType.name());
}
}
private boolean isLogEnabled() {
return L.isEnabled(L.PUMP);
}
}

View file

@ -0,0 +1,184 @@
package info.nightscout.androidaps.plugins.pump.omnipod.comm.ui;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair;
import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpDeviceState;
import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpValuesChanged;
import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.OmnipodCommunicationManager;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodCommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.PodResponseType;
import info.nightscout.androidaps.plugins.pump.omnipod.events.EventOmnipodDeviceStatusChange;
/**
* Created by andy on 4.8.2019
*/
public class OmnipodUITask {
private static final Logger LOG = LoggerFactory.getLogger(L.PUMP);
public OmnipodCommandType commandType;
public Object returnData;
private String errorDescription;
private Object[] parameters;
private PodResponseType responseType;
public OmnipodUITask(OmnipodCommandType commandType) {
this.commandType = commandType;
}
public OmnipodUITask(OmnipodCommandType commandType, Object... parameters) {
this.commandType = commandType;
this.parameters = parameters;
}
public void execute(OmnipodCommunicationManager communicationManager) {
if (isLogEnabled())
LOG.debug("OmnipodUITask: @@@ In execute. {}", commandType);
switch (commandType) {
// TODO add commands this is just sample
// case PumpModel: {
// returnData = communicationManager.getPumpModel();
// }
// break;
case InitPod:
returnData = communicationManager.initPod();
break;
case DeactivatePod:
returnData = communicationManager.deactivatePod();
break;
case ResetPodStatus:
returnData = communicationManager.resetPodStatus();
break;
case SetBasalProfile:
returnData = communicationManager.setBasalProfile((Profile) parameters[0]);
break;
case SetBolus: {
Double amount = getDoubleFromParameters(0);
if (amount != null)
returnData = communicationManager.setBolus(amount);
}
break;
case CancelBolus:
returnData = communicationManager.cancelBolus();
break;
case SetTemporaryBasal: {
TempBasalPair tbr = getTBRSettings();
if (tbr != null) {
returnData = communicationManager.setTemporaryBasal(tbr);
}
}
break;
case CancelTemporaryBasal:
returnData = communicationManager.cancelTemporaryBasal();
break;
default: {
LOG.warn("This commandType is not supported (yet) - {}.", commandType);
}
}
}
private TempBasalPair getTBRSettings() {
return new TempBasalPair(getDoubleFromParameters(0), //
false, //
getIntegerFromParameters(1));
}
private Float getFloatFromParameters(int index) {
return (Float) parameters[index];
}
public Double getDoubleFromParameters(int index) {
return (Double) parameters[index];
}
public Integer getIntegerFromParameters(int index) {
return (Integer) parameters[index];
}
public Object getResult() {
return returnData;
}
public boolean isReceived() {
return (returnData != null || errorDescription != null);
}
public void postProcess(OmnipodUIPostprocessor postprocessor) {
EventOmnipodDeviceStatusChange statusChange;
if (isLogEnabled())
LOG.debug("OmnipodUITask: @@@ In execute. {}", commandType);
if (responseType == PodResponseType.Data || responseType == PodResponseType.Acknowledgment) {
postprocessor.postProcessData(this);
}
if (responseType == PodResponseType.Invalid) {
statusChange = new EventOmnipodDeviceStatusChange(PumpDeviceState.ErrorWhenCommunicating,
"Unsupported command in OmnipodUITask");
MainApp.bus().post(statusChange);
} else if (responseType == PodResponseType.Error) {
statusChange = new EventOmnipodDeviceStatusChange(PumpDeviceState.ErrorWhenCommunicating,
errorDescription);
MainApp.bus().post(statusChange);
} else {
MainApp.bus().post(new EventMedtronicPumpValuesChanged());
MedtronicUtil.getPumpStatus().setLastCommunicationToNow();
}
MedtronicUtil.setCurrentCommand(null);
}
public boolean hasData() {
return (responseType == PodResponseType.Data || responseType == PodResponseType.Acknowledgment);
}
public Object getParameter(int index) {
return parameters[index];
}
private boolean isLogEnabled() {
return L.isEnabled(L.PUMP);
}
public PodResponseType getResponseType() {
return this.responseType;
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.plugins.pump.omnipod.defs;
/**
* Created by andy on 4.8.2019
*/
public enum OmnipodCommandType {
InitPod, //
DeactivatePod, //
SetBasalProfile, //
SetBolus, //
CancelBolus, //
SetTemporaryBasal,
CancelTemporaryBasal,
ResetPodStatus;
}

View file

@ -0,0 +1,22 @@
package info.nightscout.androidaps.plugins.pump.omnipod.defs;
import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType;
/**
* Created by andy on 4.8.2019
*/
public enum OmnipodCustomActionType implements CustomActionType {
ResetRileyLinkConfiguration(), //
InitPod(), //
DeactivatePod(), //
ResetPodStatus(), //
;
@Override
public String getKey() {
return this.name();
}
}

View file

@ -0,0 +1,13 @@
package info.nightscout.androidaps.plugins.pump.omnipod.defs;
/**
* Created by andy on 10/18/18.
*/
public enum OmnipodUIResponseType {
Data,
Error,
Invalid;
}

View file

@ -0,0 +1,38 @@
package info.nightscout.androidaps.plugins.pump.omnipod.defs;
import info.nightscout.androidaps.R;
/**
* Created by andy on 4.8.2019
*/
public enum PodDeviceState {
// FIXME
NeverContacted(R.string.medtronic_pump_status_never_contacted), //
Sleeping(R.string.medtronic_pump_status_sleeping), //
WakingUp(R.string.medtronic_pump_status_waking_up), //
Active(R.string.medtronic_pump_status_active), //
ErrorWhenCommunicating(R.string.medtronic_pump_status_error_comm), //
TimeoutWhenCommunicating(R.string.medtronic_pump_status_timeout_comm), //
// ProblemContacting(R.string.medtronic_pump_status_problem_contacting), //
PumpUnreachable(R.string.medtronic_pump_status_pump_unreachable), //
InvalidConfiguration(R.string.medtronic_pump_status_invalid_config);
Integer resourceId = null;
PodDeviceState() {
}
PodDeviceState(int resourceId) {
this.resourceId = resourceId;
}
public Integer getResourceId() {
return resourceId;
}
}

View file

@ -0,0 +1,9 @@
package info.nightscout.androidaps.plugins.pump.omnipod.defs;
public enum PodResponseType {
Acknowledgment, // set commands would just acknowledge if data was sent
Data, // query commands would return data
Error, // communication/response produced an error
Invalid // invalid response (not supported, should never be returned)
}

View file

@ -0,0 +1,47 @@
package info.nightscout.androidaps.plugins.pump.omnipod.events;
import info.nightscout.androidaps.events.Event;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState;
import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpDeviceState;
/**
* Created by andy on 4.8.2019
*/
public class EventOmnipodDeviceStatusChange extends Event {
public RileyLinkServiceState rileyLinkServiceState;
public RileyLinkError rileyLinkError;
public PumpDeviceState pumpDeviceState;
public String errorDescription;
public EventOmnipodDeviceStatusChange(RileyLinkServiceState rileyLinkServiceState) {
this(rileyLinkServiceState, null);
}
public EventOmnipodDeviceStatusChange(RileyLinkServiceState rileyLinkServiceState, RileyLinkError rileyLinkError) {
this.rileyLinkServiceState = rileyLinkServiceState;
this.rileyLinkError = rileyLinkError;
}
public EventOmnipodDeviceStatusChange(PumpDeviceState pumpDeviceState) {
this.pumpDeviceState = pumpDeviceState;
}
public EventOmnipodDeviceStatusChange(PumpDeviceState pumpDeviceState, String errorDescription) {
this.pumpDeviceState = pumpDeviceState;
this.errorDescription = errorDescription;
}
@Override
public String toString() {
return "EventOmnipodDeviceStatusChange [" + "rileyLinkServiceState=" + rileyLinkServiceState
+ ", rileyLinkError=" + rileyLinkError + ", pumpDeviceState=" + pumpDeviceState + ']';
}
}

View file

@ -0,0 +1,9 @@
package info.nightscout.androidaps.plugins.pump.omnipod.events;
import info.nightscout.androidaps.events.Event;
/**
* Created by andy on 04.06.2018.
*/
public class EventOmnipodPumpValuesChanged extends Event {
}

View file

@ -0,0 +1,157 @@
package info.nightscout.androidaps.plugins.pump.omnipod.service;
import org.joda.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.interfaces.PumpDescription;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.pump.common.data.PumpStatus;
import info.nightscout.androidaps.plugins.pump.common.data.TempBasalPair;
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState;
import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodConst;
import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodUtil;
import info.nightscout.androidaps.utils.SP;
/**
* Created by andy on 4.8.2019
*/
public class OmnipodPumpStatus extends PumpStatus {
private static Logger LOG = LoggerFactory.getLogger(L.PUMP);
public String errorDescription = null;
public String rileyLinkAddress = null;
public boolean inPreInit = true;
// statuses
public RileyLinkServiceState rileyLinkServiceState = RileyLinkServiceState.NotStarted;
public RileyLinkError rileyLinkError;
public double currentBasal = 0;
public long tempBasalStart;
public long tempBasalEnd;
public Double tempBasalAmount = 0.0d;
public Integer tempBasalLength;
private boolean rileyLinkAddressChanged = false;
private String regexMac = "([\\da-fA-F]{1,2}(?:\\:|$)){6}";
public String podNumber;
public OmnipodPumpStatus(PumpDescription pumpDescription) {
super(pumpDescription);
}
@Override
public void initSettings() {
this.activeProfileName = "";
this.reservoirRemainingUnits = 75d;
this.batteryRemaining = 75;
this.lastConnection = SP.getLong(OmnipodConst.Statistics.LastGoodPumpCommunicationTime, 0L);
this.lastDataTime = new LocalDateTime(this.lastConnection);
this.pumpType = PumpType.Insulet_Omnipod;
}
public boolean verifyConfiguration() {
try {
this.errorDescription = "-";
String rileyLinkAddress = SP.getString(RileyLinkConst.Prefs.RileyLinkAddress, null);
if (rileyLinkAddress == null) {
if (isLogEnabled())
LOG.debug("RileyLink address invalid: null");
this.errorDescription = MainApp.gs(R.string.medtronic_error_rileylink_address_invalid);
return false;
} else {
if (!rileyLinkAddress.matches(regexMac)) {
this.errorDescription = MainApp.gs(R.string.medtronic_error_rileylink_address_invalid);
if (isLogEnabled())
LOG.debug("RileyLink address invalid: {}", rileyLinkAddress);
} else {
if (!rileyLinkAddress.equals(this.rileyLinkAddress)) {
this.rileyLinkAddress = rileyLinkAddress;
rileyLinkAddressChanged = true;
}
}
}
reconfigureService();
return true;
} catch (Exception ex) {
this.errorDescription = ex.getMessage();
LOG.error("Error on Verification: " + ex.getMessage(), ex);
return false;
}
}
private boolean reconfigureService() {
if (!inPreInit && OmnipodUtil.getOmnipodService() != null) {
if (rileyLinkAddressChanged) {
OmnipodUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkNewAddressSet);
rileyLinkAddressChanged = false;
}
}
return (!rileyLinkAddressChanged);
}
public String getErrorInfo() {
verifyConfiguration();
return (this.errorDescription == null) ? "-" : this.errorDescription;
}
@Override
public void refreshConfiguration() {
verifyConfiguration();
}
public boolean setNotInPreInit() {
this.inPreInit = false;
return reconfigureService();
}
public void clearTemporaryBasal() {
this.tempBasalStart = 0L;
this.tempBasalEnd = 0L;
this.tempBasalAmount = 0.0d;
this.tempBasalLength = 0;
}
private boolean isLogEnabled() {
return L.isEnabled(L.PUMP);
}
public TempBasalPair getTemporaryBasal() {
TempBasalPair tbr = new TempBasalPair();
tbr.setDurationMinutes(tempBasalLength);
tbr.setInsulinRate(tempBasalAmount);
tbr.setStartTime(tempBasalStart);
tbr.setEndTime(tempBasalEnd);
return tbr;
}
}

View file

@ -0,0 +1,155 @@
package info.nightscout.androidaps.plugins.pump.omnipod.service;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.os.Binder;
import android.os.IBinder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkCommunicationManager;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RFSpy;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkBLE;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkEncodingType;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkService;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData;
import info.nightscout.androidaps.plugins.pump.omnipod.OmnipodPumpPlugin;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.OmnipodCommunicationManager;
import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodUtil;
import info.nightscout.androidaps.utils.SP;
/**
* Created by andy on 4.8.2019
* RileyLinkOmnipodService is intended to stay running when the gui-app is closed.
*/
public class RileyLinkOmnipodService extends RileyLinkService {
private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM);
private static RileyLinkOmnipodService instance;
OmnipodCommunicationManager omnipodCommunicationManager;
OmnipodPumpStatus pumpStatus = null;
private IBinder mBinder = new LocalBinder();
public RileyLinkOmnipodService() {
super(MainApp.instance().getApplicationContext());
instance = this;
if (isLogEnabled())
LOG.debug("RileyLinkOmnipodService newly constructed");
OmnipodUtil.setOmnipodService(this);
pumpStatus = (OmnipodPumpStatus) OmnipodPumpPlugin.getPlugin().getPumpStatusData();
}
public static RileyLinkOmnipodService getInstance() {
return instance;
}
// public static MedtronicCommunicationManager getCommunicationManager() {
// return instance.medtronicCommunicationManager;
// }
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (isLogEnabled())
LOG.warn("onConfigurationChanged");
super.onConfigurationChanged(newConfig);
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public RileyLinkEncodingType getEncoding() {
return RileyLinkEncodingType.Manchester;
}
/**
* If you have customized RileyLinkServiceData you need to override this
*/
public void initRileyLinkServiceData() {
rileyLinkServiceData = new RileyLinkServiceData(RileyLinkTargetDevice.Omnipod);
RileyLinkUtil.setRileyLinkServiceData(rileyLinkServiceData);
RileyLinkUtil.setTargetDevice(RileyLinkTargetDevice.Omnipod);
// get most recently used RileyLink address
rileyLinkServiceData.rileylinkAddress = SP.getString(RileyLinkConst.Prefs.RileyLinkAddress, "");
rileyLinkBLE = new RileyLinkBLE(this.context); // or this
rfspy = new RFSpy(rileyLinkBLE);
rfspy.startReader();
RileyLinkUtil.setRileyLinkBLE(rileyLinkBLE);
// init rileyLinkCommunicationManager
omnipodCommunicationManager = new OmnipodCommunicationManager(context, rfspy);
}
public void resetRileyLinkConfiguration() {
rfspy.resetRileyLinkConfiguration();
}
@Override
public RileyLinkCommunicationManager getDeviceCommunicationManager() {
return this.omnipodCommunicationManager;
}
public class LocalBinder extends Binder {
public RileyLinkOmnipodService getServiceInstance() {
return RileyLinkOmnipodService.this;
}
}
/* private functions */
// PumpInterface - REMOVE
public boolean isInitialized() {
return RileyLinkServiceState.isReady(RileyLinkUtil.getRileyLinkServiceData().serviceState);
}
@Override
public String getDeviceSpecificBroadcastsIdentifierPrefix() {
return null;
}
public boolean handleDeviceSpecificBroadcasts(Intent intent) {
return false;
}
@Override
public void registerDeviceSpecificBroadcasts(IntentFilter intentFilter) {
}
private boolean isLogEnabled() {
return L.isEnabled(L.PUMPCOMM);
}
}

View file

@ -0,0 +1,28 @@
package info.nightscout.androidaps.plugins.pump.omnipod.util;
/**
* Created by andy on 4.8.2019
*/
public class OmnipodConst {
static final String Prefix = "AAPS.Omnipod.";
public class Prefs {
//public static final int BatteryType = R.string.pref_key_medtronic_battery_type;
}
public class Statistics {
public static final String StatsPrefix = "omnipod_";
public static final String FirstPumpStart = Prefix + "first_pump_use";
public static final String LastGoodPumpCommunicationTime = Prefix + "lastGoodPumpCommunicationTime";
public static final String LastGoodPumpFrequency = Prefix + "LastGoodPumpFrequency";
public static final String TBRsSet = StatsPrefix + "tbrs_set";
public static final String StandardBoluses = StatsPrefix + "std_boluses_delivered";
public static final String SMBBoluses = StatsPrefix + "smb_boluses_delivered";
public static final String LastPumpHistoryEntry = StatsPrefix + "pump_history_entry";
}
}

View file

@ -0,0 +1,170 @@
package info.nightscout.androidaps.plugins.pump.omnipod.util;
import android.content.Context;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem;
import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicNotificationType;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.OmnipodCommunicationManager;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodCommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.service.OmnipodPumpStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.service.RileyLinkOmnipodService;
import info.nightscout.androidaps.utils.OKDialog;
/**
* Created by andy on 4/8/19.
*/
// FIXME
public class OmnipodUtil extends RileyLinkUtil {
private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM);
private static boolean lowLevelDebug = true;
private static RileyLinkOmnipodService omnipodService;
private static OmnipodPumpStatus omnipodPumpStatus;
private static OmnipodCommandType currentCommand;
public static Gson gsonInstance = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
public static Gson gsonInstancePretty = new GsonBuilder().excludeFieldsWithoutExposeAnnotation()
.setPrettyPrinting().create();
public static Gson getGsonInstance() {
return gsonInstance;
}
public static Gson getGsonInstancePretty() {
return gsonInstancePretty;
}
public static int makeUnsignedShort(int b2, int b1) {
int k = (b2 & 0xff) << 8 | b1 & 0xff;
return k;
}
public static byte[] getByteArrayFromUnsignedShort(int shortValue, boolean returnFixedSize) {
byte highByte = (byte) (shortValue >> 8 & 0xFF);
byte lowByte = (byte) (shortValue & 0xFF);
if (highByte > 0) {
return createByteArray(highByte, lowByte);
} else {
return returnFixedSize ? createByteArray(highByte, lowByte) : createByteArray(lowByte);
}
}
public static byte[] createByteArray(byte... data) {
return data;
}
public static byte[] createByteArray(List<Byte> data) {
byte[] array = new byte[data.size()];
for (int i = 0; i < data.size(); i++) {
array[i] = data.get(i);
}
return array;
}
public static void sendNotification(MedtronicNotificationType notificationType) {
Notification notification = new Notification( //
notificationType.getNotificationType(), //
MainApp.gs(notificationType.getResourceId()), //
notificationType.getNotificationUrgency());
MainApp.bus().post(new EventNewNotification(notification));
}
public static void sendNotification(MedtronicNotificationType notificationType, Object... parameters) {
Notification notification = new Notification( //
notificationType.getNotificationType(), //
MainApp.gs(notificationType.getResourceId(), parameters), //
notificationType.getNotificationUrgency());
MainApp.bus().post(new EventNewNotification(notification));
}
public static void dismissNotification(MedtronicNotificationType notificationType) {
MainApp.bus().post(new EventDismissNotification(notificationType.getNotificationType()));
}
public static boolean isLowLevelDebug() {
return lowLevelDebug;
}
public static void setLowLevelDebug(boolean lowLevelDebug) {
OmnipodUtil.lowLevelDebug = lowLevelDebug;
}
public static OmnipodCommunicationManager getOmnipodCommunicationManager() {
return (OmnipodCommunicationManager) RileyLinkUtil.rileyLinkCommunicationManager;
}
public static RileyLinkOmnipodService getOmnipodService() {
return OmnipodUtil.omnipodService;
}
public static void setOmnipodService(RileyLinkOmnipodService medtronicService) {
OmnipodUtil.omnipodService = medtronicService;
}
public static OmnipodCommandType getCurrentCommand() {
return OmnipodUtil.currentCommand;
}
// FIXME
public static void setCurrentCommand(OmnipodCommandType currentCommand) {
OmnipodUtil.currentCommand = currentCommand;
if (currentCommand != null)
historyRileyLink.add(new RLHistoryItem(currentCommand));
}
public static boolean isSame(Double d1, Double d2) {
double diff = d1 - d2;
return (Math.abs(diff) <= 0.000001);
}
public static void displayNotConfiguredDialog(Context context) {
OKDialog.show(context, MainApp.gs(R.string.combo_warning),
MainApp.gs(R.string.medtronic_error_operation_not_possible_no_configuration), null);
}
public static OmnipodPumpStatus getPumpStatus() {
return omnipodPumpStatus;
}
public static void setPumpStatus(OmnipodPumpStatus omnipodPumpStatus) {
OmnipodUtil.omnipodPumpStatus = omnipodPumpStatus;
}
}

View file

@ -10,16 +10,25 @@ public class Round {
}
return 0d;
}
public static Double floorTo(Double x, Double step) {
if (x != 0d) {
return Math.floor(x / step) * step;
}
return 0d;
}
public static Double ceilTo(Double x, Double step) {
if (x != 0d) {
return Math.ceil(x / step) * step;
}
return 0d;
}
public static boolean isSame(Double d1, Double d2) {
double diff = d1 - d2;
return (Math.abs(diff) <= 0.000001);
}
}

View file

@ -0,0 +1,533 @@
<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.pump.omnipod.OmnipodFragment">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/omnipod_buttons"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/overview_pumpstatuslayout_omnipod"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="5dp"
android:paddingTop="2dp"
android:visibility="gone">
<TextView
android:id="@+id/overview_pump_omnipod"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:gravity="center_vertical|center_horizontal"
android:text="@string/initializing"
android:textAppearance="?android:attr/textAppearanceSmall" />
</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="5dp"
android:background="@color/listdelimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/rileylink_status"
android:textSize="14sp" />
<TextView
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:textSize="14sp" />
<com.joanzapata.iconify.widget.IconTextView
android:id="@+id/omnipod_rl_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
android:text="{fa-bluetooth-b}"
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="5dp"
android:background="@color/listdelimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/omnipod_pod_status"
android:textSize="14sp" />
<TextView
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:textSize="14sp" />
<com.joanzapata.iconify.widget.IconTextView
android:id="@+id/omnipod_pod_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
android:text="{fa-bluetooth-b}"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<TextView
android:id="@+id/omnipod_queue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="content"
android:textAlignment="center" />
<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="5dp"
android:background="@color/listdelimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/pump_battery_label"
android:textSize="14sp" />
<TextView
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:textSize="14sp" />
<com.joanzapata.iconify.widget.IconTextView
android:id="@+id/omnipod_pumpstate_battery"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
android:textColor="@android:color/white"
android:text=""
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="5dp"
android:background="@color/listdelimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/pump_lastconnection_label"
android:textSize="14sp" />
<TextView
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:textSize="14sp" />
<TextView
android:id="@+id/omnipod_lastconnection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
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="5dp"
android:background="@color/listdelimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/pump_lastbolus_label"
android:textSize="14sp" />
<TextView
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:textSize="14sp" />
<TextView
android:id="@+id/omnipod_lastbolus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
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="5dp"
android:background="@color/listdelimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/pump_basebasalrate_label"
android:textSize="14sp" />
<TextView
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:textSize="14sp" />
<TextView
android:id="@+id/omnipod_basabasalrate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
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="5dp"
android:background="@color/listdelimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/pump_tempbasal_label"
android:textSize="14sp" />
<TextView
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:textSize="14sp" />
<TextView
android:id="@+id/omnipod_tempbasal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
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="5dp"
android:background="@color/listdelimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/pump_reservoir_label"
android:textSize="14sp" />
<TextView
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:textSize="14sp" />
<TextView
android:id="@+id/omnipod_reservoir"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
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="5dp"
android:background="@color/listdelimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="@string/medtronic_errors"
android:textSize="14sp" />
<TextView
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:textSize="14sp" />
<TextView
android:id="@+id/omnipod_errors"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
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="5dp"
android:background="@color/listdelimiter" />
</LinearLayout>
</ScrollView>
<LinearLayout
android:id="@+id/omnipod_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/omnipod_refresh"
style="@style/ButtonSmallFontStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableTop="@drawable/icon_actions_refill"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:text="@string/combo_refresh" />
<Button
android:id="@+id/omnipod_pod_mgmt"
style="@style/ButtonSmallFontStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableTop="@drawable/icon_danarhistory"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:text="@string/omnipod_pod_mgmt" />
<Button
android:id="@+id/omnipod_stats"
style="@style/ButtonSmallFontStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableTop="@drawable/icon_danarstats"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:text="RL Stats" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</FrameLayout>

View file

@ -1662,6 +1662,31 @@
<string name="slowabsorptiondetected"><![CDATA[<font color=\'%1$s\'>!!!!! Slow carbs absorption detected: %2$d%% of time. Double check your calculation. COB can be really off !!!!!</font>]]></string>
<string name="reservoirvalue">%1$.0f / %2$d U</string>
<!-- Omnipod -->
<!-- Omnipod - Base -->
<string name="omnipod_name" translatable="false">Omnipod</string>
<string name="omnipod_name_short" translatable="false">POD</string>
<string name="description_pump_omnipod">Pump integration for Omnipod, requires RileyLink (with at least 2.0 firmware) device.</string>
<!-- Omnipod - Fragment -->
<string name="omnipod_pod_mgmt">Pod Mgmt</string>
<string name="omnipod_pod_status">Pod Status</string>
<!-- Omnipod - Error -->
<string name="omnipod_error_operation_not_possible_no_configuration">Operation is not possible.\n\n You need to configure Omnipod first, before you can use this operation.</string>
<!-- Omnipod - Pod Mgmt -->
<string name="omnipod_cmd_init_pod">Init Pod</string>
<string name="omnipod_cmd_deactivate_pod">Deactivate Pod</string>
<string name="omnipod_cmd_reset_pod">Reset Pod</string>
<string name="omnipod_namex" translatable="false">Omnipod</string>
<string name="omnipod_namex2" translatable="false">Omnipod</string>
<string name="omnipod_namexxx" translatable="false">Omnipod</string>
<plurals name="objective_days">
<item quantity="one">%1$d day</item>
<item quantity="other">%1$d days</item>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/omnipod_name">
<info.nightscout.androidaps.plugins.pump.common.ui.RileyLinkSelectPreference
android:id="@+id/rileylink_mac_address_omnipod"
android:enabled="true"
android:summary=""
android:title="RileyLink Configuration"
android:key="@string/pref_key_rileylink_mac_address">
<intent android:action="info.nightscout.androidaps.plugins.PumpCommon.dialog.RileyLinkBLEScanActivity" />
</info.nightscout.androidaps.plugins.pump.common.ui.RileyLinkSelectPreference>
</PreferenceCategory>
</PreferenceScreen>