Merge pull request #5 from 0pen-dash/dash-mgr
Add OmnipodDashPodStateManager
This commit is contained in:
commit
7cb3fa8a5e
|
@ -32,7 +32,7 @@
|
|||
tools:ignore="HardcodedText" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pod_address"
|
||||
android:id="@+id/unique_id"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
|
@ -110,7 +110,7 @@
|
|||
tools:ignore="HardcodedText" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pod_tid"
|
||||
android:id="@+id/pod_sequence_number"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash
|
||||
|
||||
import info.nightscout.androidaps.events.Event
|
||||
|
||||
class EventOmnipodDashPumpValuesChanged : Event()
|
|
@ -1,213 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.android.HasAndroidInjector;
|
||||
import info.nightscout.androidaps.data.DetailedBolusInfo;
|
||||
import info.nightscout.androidaps.data.Profile;
|
||||
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||
import info.nightscout.androidaps.interfaces.CommandQueueProvider;
|
||||
import info.nightscout.androidaps.interfaces.PluginDescription;
|
||||
import info.nightscout.androidaps.interfaces.PluginType;
|
||||
import info.nightscout.androidaps.interfaces.PumpDescription;
|
||||
import info.nightscout.androidaps.interfaces.PumpInterface;
|
||||
import info.nightscout.androidaps.interfaces.PumpPluginBase;
|
||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.common.ManufacturerType;
|
||||
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.defs.PumpType;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.OmnipodDashOverviewFragment;
|
||||
import info.nightscout.androidaps.queue.commands.CustomCommand;
|
||||
import info.nightscout.androidaps.utils.TimeChangeType;
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper;
|
||||
|
||||
@Singleton
|
||||
public class OmnipodDashPumpPlugin extends PumpPluginBase implements PumpInterface {
|
||||
private static final PumpDescription PUMP_DESCRIPTION = new PumpDescription(PumpType.Omnipod_Dash);
|
||||
|
||||
private final AAPSLogger aapsLogger;
|
||||
private final ResourceHelper resourceHelper;
|
||||
private final CommandQueueProvider commandQueue;
|
||||
|
||||
@Inject
|
||||
public OmnipodDashPumpPlugin(HasAndroidInjector injector, AAPSLogger aapsLogger, ResourceHelper resourceHelper, CommandQueueProvider commandQueue) {
|
||||
super(new PluginDescription() //
|
||||
.mainType(PluginType.PUMP) //
|
||||
.fragmentClass(OmnipodDashOverviewFragment.class.getName()) //
|
||||
.pluginIcon(R.drawable.ic_pod_128)
|
||||
.pluginName(R.string.omnipod_dash_name) //
|
||||
.shortName(R.string.omnipod_dash_name_short) //
|
||||
.preferencesId(R.xml.omnipod_dash_preferences) //
|
||||
.description(R.string.omnipod_dash_pump_description), injector, aapsLogger, resourceHelper, commandQueue);
|
||||
this.aapsLogger = aapsLogger;
|
||||
this.resourceHelper = resourceHelper;
|
||||
this.commandQueue = commandQueue;
|
||||
}
|
||||
|
||||
@Override public boolean isInitialized() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public boolean isSuspended() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public boolean isBusy() {
|
||||
// prevents the queue from executing
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public boolean isConnected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public boolean isConnecting() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public boolean isHandshakeInProgress() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public void finishHandshaking() {
|
||||
|
||||
}
|
||||
|
||||
@Override public void connect(@NotNull String reason) {
|
||||
|
||||
}
|
||||
|
||||
@Override public void disconnect(@NotNull String reason) {
|
||||
|
||||
}
|
||||
|
||||
@Override public void stopConnecting() {
|
||||
|
||||
}
|
||||
|
||||
@Override public void getPumpStatus(@NotNull String reason) {
|
||||
|
||||
}
|
||||
|
||||
@NotNull @Override public PumpEnactResult setNewBasalProfile(@NotNull Profile profile) {
|
||||
// TODO
|
||||
return new PumpEnactResult(getInjector()).success(true).enacted(true);
|
||||
}
|
||||
|
||||
@Override public boolean isThisProfileSet(@NotNull Profile profile) {
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public long lastDataTime() {
|
||||
// TODO
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override public double getBaseBasalRate() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public double getReservoirLevel() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public int getBatteryLevel() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@NotNull @Override public PumpEnactResult deliverTreatment(@NotNull DetailedBolusInfo detailedBolusInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public void stopBolusDelivering() {
|
||||
|
||||
}
|
||||
|
||||
@NotNull @Override public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NotNull Profile profile, boolean enforceNew) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull @Override public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NotNull Profile profile, boolean enforceNew) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull @Override public PumpEnactResult setExtendedBolus(double insulin, int durationInMinutes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull @Override public PumpEnactResult cancelTempBasal(boolean enforceNew) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull @Override public PumpEnactResult cancelExtendedBolus() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull @Override public JSONObject getJSONStatus(@NotNull Profile profile, @NotNull String profileName, @NotNull String version) {
|
||||
return new JSONObject();
|
||||
}
|
||||
|
||||
@NotNull @Override public ManufacturerType manufacturer() {
|
||||
return getPumpDescription().pumpType.getManufacturer();
|
||||
}
|
||||
|
||||
@NotNull @Override public PumpType model() {
|
||||
return getPumpDescription().pumpType;
|
||||
}
|
||||
|
||||
@NotNull @Override public String serialNumber() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull @Override public PumpDescription getPumpDescription() {
|
||||
return PUMP_DESCRIPTION;
|
||||
}
|
||||
|
||||
@NotNull @Override public String shortStatus(boolean veryShort) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public boolean isFakingTempsByExtendedBoluses() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull @Override public PumpEnactResult loadTDDs() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public boolean canHandleDST() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CustomAction> getCustomActions() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeCustomAction(@NotNull CustomActionType customActionType) {
|
||||
aapsLogger.warn(LTag.PUMP, "Unsupported custom action: " + customActionType);
|
||||
}
|
||||
|
||||
@Nullable @Override public PumpEnactResult executeCustomCommand(@NotNull CustomCommand customCommand) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public void timezoneOrDSTChanged(@NotNull TimeChangeType timeChangeType) {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,210 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.data.DetailedBolusInfo
|
||||
import info.nightscout.androidaps.data.Profile
|
||||
import info.nightscout.androidaps.data.PumpEnactResult
|
||||
import info.nightscout.androidaps.interfaces.*
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.common.ManufacturerType
|
||||
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.defs.PumpType
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.OmnipodDashOverviewFragment
|
||||
import info.nightscout.androidaps.queue.commands.CustomCommand
|
||||
import info.nightscout.androidaps.utils.TimeChangeType
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import org.json.JSONObject
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class OmnipodDashPumpPlugin @Inject constructor(
|
||||
private val omnipodManager: OmnipodDashManager,
|
||||
private val podStateManager: OmnipodDashPodStateManager,
|
||||
injector: HasAndroidInjector,
|
||||
aapsLogger: AAPSLogger,
|
||||
resourceHelper: ResourceHelper,
|
||||
commandQueue: CommandQueueProvider
|
||||
) : PumpPluginBase(pluginDescription, injector, aapsLogger, resourceHelper, commandQueue), PumpInterface {
|
||||
|
||||
companion object {
|
||||
|
||||
private val pluginDescription = PluginDescription() //
|
||||
.mainType(PluginType.PUMP) //
|
||||
.fragmentClass(OmnipodDashOverviewFragment::class.java.name) //
|
||||
.pluginIcon(R.drawable.ic_pod_128)
|
||||
.pluginName(R.string.omnipod_dash_name) //
|
||||
.shortName(R.string.omnipod_dash_name_short) //
|
||||
.preferencesId(R.xml.omnipod_dash_preferences) //
|
||||
.description(R.string.omnipod_dash_pump_description)
|
||||
|
||||
private val pumpDescription = PumpDescription(PumpType.Omnipod_Dash)
|
||||
}
|
||||
|
||||
override fun isInitialized(): Boolean {
|
||||
// TODO
|
||||
return true
|
||||
}
|
||||
|
||||
override fun isSuspended(): Boolean {
|
||||
// TODO
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isBusy(): Boolean {
|
||||
// prevents the queue from executing commands
|
||||
// TODO
|
||||
return true
|
||||
}
|
||||
|
||||
override fun isConnected(): Boolean {
|
||||
// TODO
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isConnecting(): Boolean {
|
||||
// TODO
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isHandshakeInProgress(): Boolean {
|
||||
// TODO
|
||||
return false
|
||||
}
|
||||
|
||||
override fun finishHandshaking() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
override fun connect(reason: String) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
override fun disconnect(reason: String) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
override fun stopConnecting() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
override fun getPumpStatus(reason: String) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
override fun setNewBasalProfile(profile: Profile): PumpEnactResult {
|
||||
// TODO
|
||||
return PumpEnactResult(injector).success(true).enacted(true)
|
||||
}
|
||||
|
||||
override fun isThisProfileSet(profile: Profile): Boolean {
|
||||
// TODO
|
||||
return true
|
||||
}
|
||||
|
||||
override fun lastDataTime(): Long {
|
||||
// TODO
|
||||
return System.currentTimeMillis()
|
||||
}
|
||||
|
||||
override val baseBasalRate: Double
|
||||
get() = 0.0 // TODO
|
||||
|
||||
override val reservoirLevel: Double
|
||||
get() = 0.0 // TODO
|
||||
|
||||
override val batteryLevel: Int
|
||||
get() = 0
|
||||
|
||||
override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult {
|
||||
// TODO
|
||||
return PumpEnactResult(injector).success(false).enacted(false).comment("TODO")
|
||||
}
|
||||
|
||||
override fun stopBolusDelivering() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult {
|
||||
// TODO
|
||||
return PumpEnactResult(injector).success(false).enacted(false).comment("TODO")
|
||||
}
|
||||
|
||||
override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult {
|
||||
// TODO i18n
|
||||
return PumpEnactResult(injector).success(false).enacted(false).comment("Omnipod Dash driver does not support percentage temp basals")
|
||||
}
|
||||
|
||||
override fun setExtendedBolus(insulin: Double, durationInMinutes: Int): PumpEnactResult {
|
||||
// TODO i18n
|
||||
return PumpEnactResult(injector).success(false).enacted(false).comment("Omnipod Dash driver does not support extended boluses")
|
||||
}
|
||||
|
||||
override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult {
|
||||
// TODO
|
||||
return PumpEnactResult(injector).success(false).enacted(false).comment("TODO")
|
||||
}
|
||||
|
||||
override fun cancelExtendedBolus(): PumpEnactResult {
|
||||
// TODO i18n
|
||||
return PumpEnactResult(injector).success(false).enacted(false).comment("Omnipod Dash driver does not support extended boluses")
|
||||
}
|
||||
|
||||
override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject {
|
||||
// TODO
|
||||
return JSONObject()
|
||||
}
|
||||
|
||||
override val pumpDescription: PumpDescription = Companion.pumpDescription
|
||||
|
||||
override fun manufacturer(): ManufacturerType {
|
||||
return pumpDescription.pumpType.manufacturer
|
||||
}
|
||||
|
||||
override fun model(): PumpType {
|
||||
return pumpDescription.pumpType
|
||||
}
|
||||
|
||||
override fun serialNumber(): String {
|
||||
// TODO
|
||||
return "TODO"
|
||||
}
|
||||
|
||||
override fun shortStatus(veryShort: Boolean): String {
|
||||
// TODO
|
||||
return "TODO"
|
||||
}
|
||||
|
||||
override val isFakingTempsByExtendedBoluses: Boolean
|
||||
get() = false
|
||||
|
||||
override fun loadTDDs(): PumpEnactResult {
|
||||
// TODO i18n
|
||||
return PumpEnactResult(injector).success(false).enacted(false).comment("Omnipod Dash driver does not support TDD")
|
||||
}
|
||||
|
||||
override fun canHandleDST(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getCustomActions(): List<CustomAction> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun executeCustomAction(customActionType: CustomActionType) {
|
||||
aapsLogger.warn(LTag.PUMP, "Unsupported custom action: $customActionType")
|
||||
}
|
||||
|
||||
override fun executeCustomCommand(customCommand: CustomCommand): PumpEnactResult? {
|
||||
// TODO
|
||||
return null
|
||||
}
|
||||
|
||||
override fun timezoneOrDSTChanged(timeChangeType: TimeChangeType) {
|
||||
// TODO
|
||||
}
|
||||
}
|
|
@ -1,10 +1,16 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.dagger
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.common.dagger.ActivityScope
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.common.dagger.OmnipodWizardModule
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.BleManager
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManagerImpl
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManager
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManagerImpl
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManagerImpl
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.DashPodManagementActivity
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.OmnipodDashOverviewFragment
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.wizard.activation.DashPodActivationWizardActivity
|
||||
|
@ -31,6 +37,14 @@ abstract class OmnipodDashModule {
|
|||
@ContributesAndroidInjector
|
||||
abstract fun contributesOmnipodDashOverviewFragment(): OmnipodDashOverviewFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun contributesBleManager(): BleManager
|
||||
}
|
||||
// MANAGERS
|
||||
|
||||
@Binds
|
||||
abstract fun bindsOmnipodDashBleManagerImpl(bleManager: OmnipodDashBleManagerImpl): OmnipodDashBleManager
|
||||
|
||||
@Binds
|
||||
abstract fun bindsOmnipodDashPodStateManagerImpl(podStateManager: OmnipodDashPodStateManagerImpl): OmnipodDashPodStateManager
|
||||
|
||||
@Binds
|
||||
abstract fun bindsOmnipodDashManagerImpl(omnipodManager: OmnipodDashManagerImpl): OmnipodDashManager
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BasalProgram
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
|
||||
import io.reactivex.Observable
|
||||
|
||||
interface OmnipodDashManager {
|
||||
|
||||
fun activatePodPart1(): Observable<PodEvent>
|
||||
|
||||
fun activatePodPart2(): Observable<PodEvent>
|
||||
|
||||
fun getStatus(): Observable<PodEvent>
|
||||
|
||||
fun setBasalProgram(program: BasalProgram): Observable<PodEvent>
|
||||
|
||||
fun suspendDelivery(): Observable<PodEvent>
|
||||
|
||||
fun setTime(): Observable<PodEvent>
|
||||
|
||||
fun setTempBasal(rate: Double, durationInMinutes: Short): Observable<PodEvent>
|
||||
|
||||
fun cancelTempBasal(): Observable<PodEvent>
|
||||
|
||||
fun bolus(amount: Double): Observable<PodEvent>
|
||||
|
||||
fun cancelBolus(): Observable<PodEvent>
|
||||
|
||||
fun silenceAlerts(): Observable<PodEvent>
|
||||
|
||||
fun deactivatePod(): Observable<PodEvent>
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver
|
||||
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManager
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BasalProgram
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
|
||||
import io.reactivex.Observable
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class OmnipodDashManagerImpl @Inject constructor(
|
||||
private val aapsLogger: AAPSLogger,
|
||||
private val podStateManager: OmnipodDashPodStateManager,
|
||||
private val bleManager: OmnipodDashBleManager
|
||||
) : OmnipodDashManager {
|
||||
|
||||
override fun activatePodPart1(): Observable<PodEvent> {
|
||||
// TODO
|
||||
return Observable.empty()
|
||||
}
|
||||
|
||||
override fun activatePodPart2(): Observable<PodEvent> {
|
||||
// TODO
|
||||
return Observable.empty()
|
||||
}
|
||||
|
||||
override fun getStatus(): Observable<PodEvent> {
|
||||
// TODO
|
||||
return Observable.empty()
|
||||
}
|
||||
|
||||
override fun setBasalProgram(program: BasalProgram): Observable<PodEvent> {
|
||||
// TODO
|
||||
return Observable.empty()
|
||||
}
|
||||
|
||||
override fun suspendDelivery(): Observable<PodEvent> {
|
||||
// TODO
|
||||
return Observable.empty()
|
||||
}
|
||||
|
||||
override fun setTime(): Observable<PodEvent> {
|
||||
// TODO
|
||||
return Observable.empty()
|
||||
}
|
||||
|
||||
override fun setTempBasal(rate: Double, durationInMinutes: Short): Observable<PodEvent> {
|
||||
// TODO
|
||||
return Observable.empty()
|
||||
}
|
||||
|
||||
override fun cancelTempBasal(): Observable<PodEvent> {
|
||||
// TODO
|
||||
return Observable.empty()
|
||||
}
|
||||
|
||||
override fun bolus(amount: Double): Observable<PodEvent> {
|
||||
// TODO
|
||||
return Observable.empty()
|
||||
}
|
||||
|
||||
override fun cancelBolus(): Observable<PodEvent> {
|
||||
// TODO
|
||||
return Observable.empty()
|
||||
}
|
||||
|
||||
override fun silenceAlerts(): Observable<PodEvent> {
|
||||
// TODO
|
||||
return Observable.empty()
|
||||
}
|
||||
|
||||
override fun deactivatePod(): Observable<PodEvent> {
|
||||
// TODO
|
||||
return Observable.empty()
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm;
|
||||
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
|
||||
public class BleCommCallbacks extends BluetoothGattCallback {
|
||||
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.blecommand.BleCommand;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.blecommand.BleCommandHello;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.blecommand.BleCommandType;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CharacteristicNotFoundException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotSendBleCmdException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotSendBleException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.FailedToConnectException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ScanFailException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ServiceNotFoundException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan.PodScanner;
|
||||
|
||||
@Singleton
|
||||
public class BleManager implements OmnipodDashCommunicationManager {
|
||||
private static final int CONNECT_TIMEOUT_MS = 5000;
|
||||
private static final int DISCOVER_SERVICES_TIMEOUT_MS = 5000;
|
||||
private static final String SERVICE_UUID = "1a7e-4024-e3ed-4464-8b7e-751e03d0dc5f";
|
||||
private static final String CMD_CHARACTERISTIC_UUID = "1a7e-2441-e3ed-4464-8b7e-751e03d0dc5f";
|
||||
private static final String DATA_CHARACTERISTIC_UUID = "1a7e-2442-e3ed-4464-8b7e-751e03d0dc5f";
|
||||
private static final int CONTROLLER_ID = 4242; // TODO read from preferences or somewhere else.
|
||||
private static BleManager instance = null;
|
||||
private final Context context;
|
||||
private final BluetoothAdapter bluetoothAdapter;
|
||||
private final BluetoothManager bluetoothManager;
|
||||
@Inject AAPSLogger aapsLogger;
|
||||
private String podAddress;
|
||||
private BluetoothGatt gatt;
|
||||
|
||||
private BluetoothGattCharacteristic cmdCharacteristic;
|
||||
private BluetoothGattCharacteristic dataCharacteristic;
|
||||
|
||||
@Inject
|
||||
public BleManager(Context context) {
|
||||
this.context = context;
|
||||
this.bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||
this.bluetoothAdapter = bluetoothManager.getAdapter();
|
||||
}
|
||||
|
||||
public static BleManager getInstance(Context context) {
|
||||
BleManager ret;
|
||||
synchronized (BleManager.class) {
|
||||
if (instance == null) {
|
||||
instance = new BleManager(context);
|
||||
}
|
||||
ret = instance;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static UUID uuidFromString(String s) {
|
||||
return new UUID(
|
||||
new BigInteger(s.replace("-", "").substring(0, 16), 16).longValue(),
|
||||
new BigInteger(s.replace("-", "").substring(16), 16).longValue()
|
||||
);
|
||||
}
|
||||
|
||||
public void activateNewPod()
|
||||
throws InterruptedException,
|
||||
ScanFailException,
|
||||
FailedToConnectException,
|
||||
CouldNotSendBleException {
|
||||
this.aapsLogger.info(LTag.PUMPBTCOMM, "starting new pod activation");
|
||||
PodScanner podScanner = new PodScanner(this.aapsLogger, this.bluetoothAdapter);
|
||||
this.podAddress = podScanner.scanForPod(PodScanner.SCAN_FOR_SERVICE_UUID, PodScanner.POD_ID_NOT_ACTIVATED).getScanResult().getDevice().getAddress();
|
||||
// For tests: this.podAddress = "B8:27:EB:1D:7E:BB";
|
||||
this.connect();
|
||||
// do the dance: send SP0, SP1, etc
|
||||
// get and save LTK
|
||||
}
|
||||
|
||||
public void connect()
|
||||
throws FailedToConnectException,
|
||||
CouldNotSendBleException {
|
||||
// TODO: locking?
|
||||
|
||||
BluetoothDevice podDevice = this.bluetoothAdapter.getRemoteDevice(this.podAddress);
|
||||
BluetoothGattCallback bleCommCallback = new BleCommCallbacks();
|
||||
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to " + this.podAddress);
|
||||
gatt = podDevice.connectGatt(this.context, true, bleCommCallback, BluetoothDevice.TRANSPORT_LE);
|
||||
|
||||
try {
|
||||
Thread.sleep(CONNECT_TIMEOUT_MS);
|
||||
} catch (InterruptedException e) {
|
||||
// we get interrupted on successful connection
|
||||
// TODO: interrupt this thread onConnect()
|
||||
}
|
||||
|
||||
int connectionState = this.bluetoothManager.getConnectionState(podDevice, BluetoothProfile.GATT);
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "GATT connection state: " + connectionState);
|
||||
if (connectionState != BluetoothProfile.STATE_CONNECTED) {
|
||||
throw new FailedToConnectException(this.podAddress);
|
||||
}
|
||||
this.discoverServicesAndSayHello(gatt);
|
||||
|
||||
}
|
||||
|
||||
private void discoverServicesAndSayHello(BluetoothGatt gatt)
|
||||
throws FailedToConnectException,
|
||||
CouldNotSendBleException {
|
||||
gatt.discoverServices();
|
||||
try {
|
||||
Thread.sleep(CONNECT_TIMEOUT_MS);
|
||||
} catch (InterruptedException e) {
|
||||
// we get interrupted on successfull connection
|
||||
// TODO: interrupt this thread onConnect()
|
||||
}
|
||||
|
||||
BluetoothGattService service = gatt.getService(uuidFromString(SERVICE_UUID));
|
||||
if (service == null) {
|
||||
throw new ServiceNotFoundException(SERVICE_UUID);
|
||||
}
|
||||
BluetoothGattCharacteristic cmdChar = service.getCharacteristic(uuidFromString(CMD_CHARACTERISTIC_UUID));
|
||||
if (cmdChar == null) {
|
||||
throw new CharacteristicNotFoundException(CMD_CHARACTERISTIC_UUID);
|
||||
}
|
||||
BluetoothGattCharacteristic dataChar = service.getCharacteristic(uuidFromString(DATA_CHARACTERISTIC_UUID));
|
||||
if (dataChar == null) {
|
||||
throw new CharacteristicNotFoundException(DATA_CHARACTERISTIC_UUID);
|
||||
}
|
||||
this.cmdCharacteristic = cmdChar;
|
||||
this.dataCharacteristic = dataChar;
|
||||
|
||||
BleCommand hello = new BleCommandHello(CONTROLLER_ID);
|
||||
if (!this.sendCmd(hello.asByteArray())) {
|
||||
throw new CouldNotSendBleCmdException();
|
||||
}
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "saying hello to the pod" + hello.asByteArray());
|
||||
|
||||
}
|
||||
|
||||
private boolean sendCmd(byte[] payload) {
|
||||
// TODO move out of here
|
||||
this.cmdCharacteristic.setValue(payload);
|
||||
boolean ret = this.gatt.writeCharacteristic(cmdCharacteristic);
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Sending command status. data:" + payload.toString() + "status: " + ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm
|
||||
|
||||
import java.math.BigInteger
|
||||
import java.util.*
|
||||
|
||||
enum class CharacteristicType(val value: String) {
|
||||
CMD("1a7e2441-e3ed-4464-8b7e-751e03d0dc5f"), DATA("1a7e2442-e3ed-4464-8b7e-751e03d0dc5f");
|
||||
|
||||
val uuid: UUID
|
||||
get() = UUID(
|
||||
BigInteger(value.replace("-", "").substring(0, 16), 16).toLong(),
|
||||
BigInteger(value.replace("-", "").substring(16), 16).toLong()
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun byValue(value: String): CharacteristicType =
|
||||
values().firstOrNull { it.value == value }
|
||||
?: throw IllegalArgumentException("Unknown Characteristic Type: $value")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm
|
||||
|
||||
interface OmnipodDashBleManager {
|
||||
|
||||
// TODO should we keep this method?
|
||||
fun activateNewPod()
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.bluetooth.BluetoothManager
|
||||
import android.bluetooth.BluetoothProfile
|
||||
import android.content.Context
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.BuildConfig
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.BleCommCallbacks
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command.BleCommandHello
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.*
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.BleIO
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan.PodScanner
|
||||
import java.util.concurrent.BlockingQueue
|
||||
import java.util.concurrent.LinkedBlockingDeque
|
||||
import java.util.concurrent.TimeoutException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class OmnipodDashBleManagerImpl @Inject constructor(private val context: Context, private val aapsLogger: AAPSLogger) : OmnipodDashBleManager {
|
||||
|
||||
private val bluetoothManager: BluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
||||
private val bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter
|
||||
|
||||
@Throws(InterruptedException::class, ScanFailException::class, FailedToConnectException::class, CouldNotSendBleException::class, BleIOBusyException::class, TimeoutException::class, CouldNotConfirmWriteException::class, CouldNotEnableNotifications::class, DescriptorNotFoundException::class, CouldNotConfirmDescriptorWriteException::class)
|
||||
override fun activateNewPod() {
|
||||
aapsLogger.info(LTag.PUMPBTCOMM, "starting new pod activation")
|
||||
val podScanner = PodScanner(aapsLogger, bluetoothAdapter)
|
||||
val podAddress = podScanner.scanForPod(PodScanner.SCAN_FOR_SERVICE_UUID, PodScanner.POD_ID_NOT_ACTIVATED).scanResult.device.address
|
||||
// For tests: this.podAddress = "B8:27:EB:1D:7E:BB";
|
||||
connect(podAddress)
|
||||
}
|
||||
|
||||
@Throws(FailedToConnectException::class, CouldNotSendBleException::class, InterruptedException::class, BleIOBusyException::class, TimeoutException::class, CouldNotConfirmWriteException::class, CouldNotEnableNotifications::class, DescriptorNotFoundException::class, CouldNotConfirmDescriptorWriteException::class)
|
||||
private fun connect(podAddress: String) {
|
||||
// TODO: locking?
|
||||
val podDevice = bluetoothAdapter.getRemoteDevice(podAddress)
|
||||
val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>> =
|
||||
mapOf(CharacteristicType.CMD to LinkedBlockingDeque(),
|
||||
CharacteristicType.DATA to LinkedBlockingDeque());
|
||||
val bleCommCallbacks = BleCommCallbacks(aapsLogger, incomingPackets)
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to $podAddress")
|
||||
var autoConnect = true
|
||||
if (BuildConfig.DEBUG) {
|
||||
autoConnect = false
|
||||
// TODO: remove this in the future
|
||||
// it's easier to start testing from scratch on each run.
|
||||
}
|
||||
val gatt = podDevice.connectGatt(context, autoConnect, bleCommCallbacks, BluetoothDevice.TRANSPORT_LE)
|
||||
bleCommCallbacks.waitForConnection(CONNECT_TIMEOUT_MS)
|
||||
val connectionState = bluetoothManager.getConnectionState(podDevice, BluetoothProfile.GATT)
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "GATT connection state: $connectionState")
|
||||
if (connectionState != BluetoothProfile.STATE_CONNECTED) {
|
||||
throw FailedToConnectException(podAddress)
|
||||
}
|
||||
val discoverer = ServiceDiscoverer(aapsLogger, gatt, bleCommCallbacks)
|
||||
val chars = discoverer.discoverServices()
|
||||
val bleIO = BleIO(aapsLogger, chars, incomingPackets, gatt, bleCommCallbacks)
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Saying hello to the pod")
|
||||
bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandHello(CONTROLLER_ID).data)
|
||||
bleIO.readyToRead()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val CONNECT_TIMEOUT_MS = 5000
|
||||
private const val CONTROLLER_ID = 4242 // TODO read from preferences or somewhere else.
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm
|
||||
|
||||
import android.bluetooth.BluetoothGatt
|
||||
import android.bluetooth.BluetoothGattCharacteristic
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.BleCommCallbacks
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CharacteristicNotFoundException
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ServiceNotFoundException
|
||||
import java.math.BigInteger
|
||||
import java.util.*
|
||||
|
||||
class ServiceDiscoverer(private val logger: AAPSLogger, private val gatt: BluetoothGatt, private val bleCallbacks: BleCommCallbacks) {
|
||||
|
||||
/***
|
||||
* This is first step after connection establishment
|
||||
*/
|
||||
@Throws(InterruptedException::class, ServiceNotFoundException::class, CharacteristicNotFoundException::class)
|
||||
fun discoverServices(): Map<CharacteristicType, BluetoothGattCharacteristic> {
|
||||
logger.debug(LTag.PUMPBTCOMM, "Discovering services")
|
||||
gatt.discoverServices()
|
||||
bleCallbacks.waitForServiceDiscovery(DISCOVER_SERVICES_TIMEOUT_MS)
|
||||
logger.debug(LTag.PUMPBTCOMM, "Services discovered")
|
||||
val service = gatt.getService(SERVICE_UUID.toUuid())
|
||||
?: throw ServiceNotFoundException(SERVICE_UUID)
|
||||
val cmdChar = service.getCharacteristic(CharacteristicType.CMD.uuid)
|
||||
?: throw CharacteristicNotFoundException(CharacteristicType.CMD.value)
|
||||
val dataChar = service.getCharacteristic(CharacteristicType.DATA.uuid) // TODO: this is never used
|
||||
?: throw CharacteristicNotFoundException(CharacteristicType.DATA.value)
|
||||
var chars = mapOf(CharacteristicType.CMD to cmdChar,
|
||||
CharacteristicType.DATA to dataChar)
|
||||
return chars
|
||||
}
|
||||
|
||||
|
||||
private fun String.toUuid(): UUID = UUID(
|
||||
BigInteger(replace("-", "").substring(0, 16), 16).toLong(),
|
||||
BigInteger(replace("-", "").substring(16), 16).toLong()
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val SERVICE_UUID = "1a7e-4024-e3ed-4464-8b7e-751e03d0dc5f"
|
||||
private const val DISCOVER_SERVICES_TIMEOUT_MS = 5000
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.blecommand;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class BleCommand {
|
||||
private final byte[] data;
|
||||
|
||||
public BleCommand(@NotNull BleCommandType type) {
|
||||
this.data = new byte[]{type.getValue()};
|
||||
}
|
||||
|
||||
public BleCommand(@NotNull BleCommandType type, @NotNull byte[] payload) {
|
||||
int n = payload.length + 1;
|
||||
this.data = new byte[n];
|
||||
this.data[0] = type.getValue();
|
||||
System.arraycopy(payload, 0, data, 1, payload.length);
|
||||
}
|
||||
|
||||
public byte[] asByteArray() {
|
||||
return this.data;
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.blecommand;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class BleCommandHello extends BleCommand {
|
||||
public BleCommandHello(int controllerId) {
|
||||
super(BleCommandType.HELLO,
|
||||
ByteBuffer.allocate(6)
|
||||
.put((byte) 1) // TODO find the meaning of this constant
|
||||
.put((byte) 4) // TODO find the meaning of this constant
|
||||
.putInt(controllerId).array()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.blecommand;
|
||||
|
||||
public enum BleCommandType {
|
||||
RTS((byte) 0x00),
|
||||
CTS((byte) 0x01),
|
||||
NACK((byte) 0x02),
|
||||
ABORT((byte) 0x03),
|
||||
SUCCESS((byte) 0x04),
|
||||
FAIL((byte) 0x05),
|
||||
HELLO((byte) 0x06);
|
||||
|
||||
public final byte value;
|
||||
|
||||
BleCommandType(byte value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static BleCommandType byValue(byte value) {
|
||||
for (BleCommandType type : values()) {
|
||||
if (type.value == value) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown BleCommandType: " + value);
|
||||
}
|
||||
|
||||
public byte getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks
|
||||
|
||||
import android.bluetooth.BluetoothGatt
|
||||
import android.bluetooth.BluetoothGattCallback
|
||||
import android.bluetooth.BluetoothGattCharacteristic
|
||||
import android.bluetooth.BluetoothGattDescriptor
|
||||
import android.bluetooth.BluetoothProfile
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.CharacteristicType
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.CharacteristicType.Companion.byValue
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotConfirmDescriptorWriteException
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotConfirmWriteException
|
||||
import java.util.concurrent.BlockingQueue
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
class BleCommCallbacks(private val aapsLogger: AAPSLogger, private val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>>) : BluetoothGattCallback() {
|
||||
|
||||
private val serviceDiscoveryComplete: CountDownLatch = CountDownLatch(1)
|
||||
private val connected: CountDownLatch = CountDownLatch(1)
|
||||
private val writeQueue: BlockingQueue<CharacteristicWriteConfirmation> = LinkedBlockingQueue(1)
|
||||
private val descriptorWriteQueue: BlockingQueue<DescriptorWriteConfirmation> = LinkedBlockingQueue(1)
|
||||
|
||||
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
|
||||
super.onConnectionStateChange(gatt, status, newState)
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "OnConnectionStateChange discovered with status/state$status/$newState")
|
||||
if (newState == BluetoothProfile.STATE_CONNECTED && status == BluetoothGatt.GATT_SUCCESS) {
|
||||
connected.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
|
||||
super.onServicesDiscovered(gatt, status)
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "OnServicesDiscovered with status$status")
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
serviceDiscoveryComplete.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(InterruptedException::class)
|
||||
fun waitForConnection(timeout_ms: Int) {
|
||||
connected.await(timeout_ms.toLong(), TimeUnit.MILLISECONDS)
|
||||
}
|
||||
|
||||
@Throws(InterruptedException::class)
|
||||
fun waitForServiceDiscovery(timeout_ms: Int) {
|
||||
serviceDiscoveryComplete.await(timeout_ms.toLong(), TimeUnit.MILLISECONDS)
|
||||
}
|
||||
|
||||
@Throws(InterruptedException::class, TimeoutException::class, CouldNotConfirmWriteException::class)
|
||||
fun confirmWrite(expectedPayload: ByteArray, timeout_ms: Int) {
|
||||
val received: CharacteristicWriteConfirmation = writeQueue.poll(timeout_ms.toLong(), TimeUnit.MILLISECONDS)
|
||||
?: throw TimeoutException()
|
||||
|
||||
when (received) {
|
||||
is CharacteristicWriteConfirmationPayload -> confirmWritePayload(expectedPayload, received)
|
||||
is CharacteristicWriteConfirmationError -> throw CouldNotConfirmWriteException(received.status)
|
||||
}
|
||||
}
|
||||
|
||||
private fun confirmWritePayload(expectedPayload: ByteArray, received: CharacteristicWriteConfirmationPayload) {
|
||||
if (!expectedPayload.contentEquals(received.payload)) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Could not confirm write. Got " + received.payload + ".Excepted: " + expectedPayload)
|
||||
throw CouldNotConfirmWriteException(expectedPayload, received.payload)
|
||||
}
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Confirmed write with value: " + received.payload)
|
||||
}
|
||||
|
||||
override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
|
||||
super.onCharacteristicWrite(gatt, characteristic, status)
|
||||
val writeConfirmation = if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
CharacteristicWriteConfirmationPayload(characteristic.value)
|
||||
} else {
|
||||
CharacteristicWriteConfirmationError(status)
|
||||
}
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "OnCharacteristicWrite with status/char/value " +
|
||||
status + "/" + byValue(characteristic.uuid.toString()) + "/" + characteristic.value)
|
||||
try {
|
||||
if (writeQueue.size > 0) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Write confirm queue should be empty. found: " + writeQueue.size)
|
||||
writeQueue.clear()
|
||||
}
|
||||
val offered = writeQueue.offer(writeConfirmation, WRITE_CONFIRM_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
|
||||
if (!offered) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Received delayed write confirmation")
|
||||
}
|
||||
} catch (e: InterruptedException) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Interrupted while sending write confirmation")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
|
||||
super.onCharacteristicChanged(gatt, characteristic)
|
||||
val payload = characteristic.value
|
||||
val characteristicType = byValue(characteristic.uuid.toString())
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "OnCharacteristicChanged with char/value " +
|
||||
characteristicType + "/" +
|
||||
payload)
|
||||
incomingPackets[characteristicType]!!.add(payload)
|
||||
}
|
||||
|
||||
@Throws(InterruptedException::class, CouldNotConfirmDescriptorWriteException::class)
|
||||
fun confirmWriteDescriptor(descriptorUUID: String, timeout_ms: Int) {
|
||||
val confirmed: DescriptorWriteConfirmation = descriptorWriteQueue.poll(timeout_ms.toLong(), TimeUnit.MILLISECONDS)
|
||||
?: throw TimeoutException()
|
||||
when (confirmed) {
|
||||
is DescriptorWriteConfirmationError -> throw CouldNotConfirmWriteException(confirmed.status)
|
||||
is DescriptorWriteConfirmationUUID -> if (confirmed.uuid != descriptorUUID) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Could not confirm descriptor write. Got ${confirmed.uuid}. Expected: ${descriptorUUID}")
|
||||
throw CouldNotConfirmDescriptorWriteException(descriptorUUID, confirmed.uuid)
|
||||
} else {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Confirmed descriptor write : " + confirmed.uuid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
|
||||
super.onDescriptorWrite(gatt, descriptor, status)
|
||||
val writeConfirmation = if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "OnDescriptor value " + descriptor.value)
|
||||
DescriptorWriteConfirmationUUID(descriptor.uuid.toString())
|
||||
} else {
|
||||
DescriptorWriteConfirmationError(status)
|
||||
}
|
||||
try {
|
||||
if (descriptorWriteQueue.size > 0) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Descriptor write queue should be empty, found: " + descriptorWriteQueue.size)
|
||||
descriptorWriteQueue.clear()
|
||||
}
|
||||
val offered = descriptorWriteQueue.offer(writeConfirmation, WRITE_CONFIRM_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
|
||||
if (!offered) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Received delayed descriptor write confirmation")
|
||||
}
|
||||
} catch (e: InterruptedException) {
|
||||
aapsLogger.warn(LTag.PUMPBTCOMM, "Interrupted while sending descriptor write confirmation")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val WRITE_CONFIRM_TIMEOUT_MS = 10 // the confirmation queue should be empty anyway
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks
|
||||
|
||||
sealed class CharacteristicWriteConfirmation
|
||||
|
||||
data class CharacteristicWriteConfirmationPayload(val payload: ByteArray) : CharacteristicWriteConfirmation()
|
||||
|
||||
data class CharacteristicWriteConfirmationError(val status: Int) : CharacteristicWriteConfirmation()
|
|
@ -0,0 +1,7 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks
|
||||
|
||||
sealed class DescriptorWriteConfirmation
|
||||
|
||||
data class DescriptorWriteConfirmationUUID(val uuid: String): DescriptorWriteConfirmation()
|
||||
|
||||
data class DescriptorWriteConfirmationError(val status: Int): DescriptorWriteConfirmation()
|
|
@ -0,0 +1,17 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command
|
||||
|
||||
abstract class BleCommand {
|
||||
|
||||
val data: ByteArray
|
||||
|
||||
constructor(type: BleCommandType) {
|
||||
data = byteArrayOf(type.value)
|
||||
}
|
||||
|
||||
constructor(type: BleCommandType, payload: ByteArray) {
|
||||
val n = payload.size + 1
|
||||
data = ByteArray(n)
|
||||
data[0] = type.value
|
||||
System.arraycopy(payload, 0, data, 1, payload.size)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class BleCommandHello(controllerId: Int) : BleCommand(BleCommandType.HELLO,
|
||||
ByteBuffer.allocate(6)
|
||||
.put(1.toByte()) // TODO find the meaning of this constant
|
||||
.put(4.toByte()) // TODO find the meaning of this constant
|
||||
.putInt(controllerId).array()
|
||||
)
|
|
@ -0,0 +1,13 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command
|
||||
|
||||
enum class BleCommandType(val value: Byte) {
|
||||
RTS(0x00.toByte()), CTS(0x01.toByte()), NACK(0x02.toByte()), ABORT(0x03.toByte()), SUCCESS(0x04.toByte()), FAIL(0x05.toByte()), HELLO(0x06.toByte());
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun byValue(value: Byte): BleCommandType =
|
||||
values().firstOrNull { it.value == value }
|
||||
?: throw IllegalArgumentException("Unknown BleCommandType: $value")
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm;
|
||||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
public interface OmnipodDashCommunicationManager {
|
||||
}
|
||||
class BleIOBusyException : Exception()
|
|
@ -1,7 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||
|
||||
public class CharacteristicNotFoundException extends FailedToConnectException {
|
||||
public CharacteristicNotFoundException(String cmdCharacteristicUuid) {
|
||||
super("characteristic not found: " + cmdCharacteristicUuid);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
class CharacteristicNotFoundException(cmdCharacteristicUuid: String) : FailedToConnectException("characteristic not found: $cmdCharacteristicUuid")
|
|
@ -0,0 +1,6 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
class CouldNotConfirmDescriptorWriteException(override val message: String?) : Exception(message) {
|
||||
constructor(sent: String, confirmed: String): this("Could not confirm write. Sent: {$sent} .Received: ${confirmed}")
|
||||
constructor(status: Int): this("Could not confirm write. Write status: ${status}")
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
class CouldNotConfirmWriteException(override val message: String?) : Exception(message) {
|
||||
constructor(sent: ByteArray, confirmed: ByteArray): this("Could not confirm write. Sent: {$sent} .Received: ${confirmed}")
|
||||
constructor(status: Int): this("Could not confirm write. Write status: ${status}")
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.CharacteristicType
|
||||
|
||||
class CouldNotEnableNotifications(cmd: CharacteristicType) : Exception(cmd.value)
|
|
@ -1,4 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||
|
||||
public class CouldNotSendBleCmdException extends CouldNotSendBleException {
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||
|
||||
public class CouldNotSendBleException extends Exception {
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
class CouldNotSendBleException(msg: String?) : Exception(msg)
|
|
@ -0,0 +1,3 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
class DescriptorNotFoundException : Exception()
|
|
@ -1,11 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||
|
||||
import android.os.ParcelUuid;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DiscoveredInvalidPodException extends Exception {
|
||||
public DiscoveredInvalidPodException(String message, List<ParcelUuid> serviceUUIds) {
|
||||
super(message + " service UUIDs: " + serviceUUIds);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
import android.os.ParcelUuid
|
||||
|
||||
class DiscoveredInvalidPodException: Exception {
|
||||
constructor(message: String) : super(message) {}
|
||||
constructor(message: String, serviceUUIds: List<ParcelUuid?>) : super("$message service UUIDs: $serviceUUIds"){}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||
|
||||
public class FailedToConnectException extends Exception {
|
||||
public FailedToConnectException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FailedToConnectException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
open class FailedToConnectException : Exception {
|
||||
constructor() : super() {}
|
||||
constructor(message: String?) : super(message) {}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||
|
||||
public class ScanFailException extends Exception {
|
||||
public ScanFailException() {
|
||||
}
|
||||
|
||||
public ScanFailException(int errorCode) {
|
||||
super("errorCode" + errorCode);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
open class ScanFailException : Exception {
|
||||
constructor() {}
|
||||
constructor(errorCode: Int) : super("errorCode$errorCode") {}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan.BleDiscoveredDevice;
|
||||
|
||||
public class ScanFailFoundTooManyException extends ScanFailException {
|
||||
private final List<BleDiscoveredDevice> devices;
|
||||
|
||||
public ScanFailFoundTooManyException(List<BleDiscoveredDevice> devices) {
|
||||
super();
|
||||
this.devices = new ArrayList<>(devices);
|
||||
}
|
||||
|
||||
public List<BleDiscoveredDevice> getDiscoveredDevices() {
|
||||
return Collections.unmodifiableList(this.devices);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan.BleDiscoveredDevice
|
||||
import java.util.*
|
||||
|
||||
class ScanFailFoundTooManyException(devices: List<BleDiscoveredDevice>) : ScanFailException() {
|
||||
|
||||
private val devices: List<BleDiscoveredDevice> = ArrayList(devices)
|
||||
val discoveredDevices: List<BleDiscoveredDevice>
|
||||
get() = Collections.unmodifiableList(devices)
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||
|
||||
public class ScanFailNotFoundException extends ScanFailException {
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
class ScanFailNotFoundException : ScanFailException()
|
|
@ -1,7 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||
|
||||
public class ServiceNotFoundException extends FailedToConnectException {
|
||||
public ServiceNotFoundException(String serviceUuid) {
|
||||
super("service not found: " + serviceUuid);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
|
||||
|
||||
class ServiceNotFoundException(serviceUuid: String) : FailedToConnectException("service not found: $serviceUuid")
|
|
@ -0,0 +1,100 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io
|
||||
|
||||
import android.bluetooth.BluetoothGatt
|
||||
import android.bluetooth.BluetoothGattCharacteristic
|
||||
import android.bluetooth.BluetoothGattDescriptor
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.CharacteristicType
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.BleCommCallbacks
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.*
|
||||
import java.util.concurrent.BlockingQueue
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
class BleIO(private val aapsLogger: AAPSLogger, private val chars: Map<CharacteristicType, BluetoothGattCharacteristic>, private val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>>, private val gatt: BluetoothGatt, private val bleCommCallbacks: BleCommCallbacks) {
|
||||
|
||||
private var state: IOState = IOState.IDLE
|
||||
|
||||
/***
|
||||
*
|
||||
* @param characteristic where to read from(CMD or DATA)
|
||||
* @return a byte array with the received data
|
||||
*/
|
||||
@Throws(BleIOBusyException::class, InterruptedException::class, TimeoutException::class)
|
||||
fun receivePacket(characteristic: CharacteristicType): ByteArray {
|
||||
synchronized(state) {
|
||||
if (state != IOState.IDLE) {
|
||||
throw BleIOBusyException()
|
||||
}
|
||||
state = IOState.READING
|
||||
}
|
||||
val ret = incomingPackets[characteristic]?.poll(DEFAULT_IO_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
|
||||
?: throw TimeoutException()
|
||||
synchronized(state) { state = IOState.IDLE }
|
||||
return ret
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* @param characteristic where to write to(CMD or DATA)
|
||||
* @param payload the data to send
|
||||
* @throws CouldNotSendBleException
|
||||
*/
|
||||
@Throws(CouldNotSendBleException::class, BleIOBusyException::class, InterruptedException::class, CouldNotConfirmWriteException::class, TimeoutException::class)
|
||||
fun sendAndConfirmPacket(characteristic: CharacteristicType, payload: ByteArray) {
|
||||
synchronized(state) {
|
||||
if (state != IOState.IDLE) {
|
||||
throw BleIOBusyException()
|
||||
}
|
||||
state = IOState.WRITING
|
||||
}
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "BleIO: Sending data on" + characteristic.name + "/" + payload.toString())
|
||||
val ch = chars[characteristic]
|
||||
val set = ch!!.setValue(payload)
|
||||
if (!set) {
|
||||
throw CouldNotSendBleException("setValue")
|
||||
}
|
||||
val sent = gatt.writeCharacteristic(ch)
|
||||
if (!sent) {
|
||||
throw CouldNotSendBleException("writeCharacteristic")
|
||||
}
|
||||
bleCommCallbacks.confirmWrite(payload, DEFAULT_IO_TIMEOUT_MS)
|
||||
synchronized(state) { state = IOState.IDLE }
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before sending a new message.
|
||||
* The incoming queues should be empty, so we log when they are not.
|
||||
*/
|
||||
fun flushIncomingQueues() {}
|
||||
|
||||
/**
|
||||
* Enable intentions on the characteristics.
|
||||
* This will signal the pod it can start sending back data
|
||||
* @return
|
||||
*/
|
||||
@Throws(CouldNotSendBleException::class, CouldNotEnableNotifications::class, DescriptorNotFoundException::class, InterruptedException::class, CouldNotConfirmDescriptorWriteException::class)
|
||||
fun readyToRead() {
|
||||
for (type in CharacteristicType.values()) {
|
||||
val ch = chars[type]
|
||||
val notificationSet = gatt.setCharacteristicNotification(ch, true)
|
||||
if (!notificationSet) {
|
||||
throw CouldNotEnableNotifications(type)
|
||||
}
|
||||
val descriptors = ch!!.descriptors
|
||||
if (descriptors.size != 1) {
|
||||
throw DescriptorNotFoundException()
|
||||
}
|
||||
val descriptor = descriptors[0]
|
||||
descriptor.value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
|
||||
gatt.writeDescriptor(descriptor)
|
||||
bleCommCallbacks.confirmWriteDescriptor(descriptor.uuid.toString(), DEFAULT_IO_TIMEOUT_MS)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val DEFAULT_IO_TIMEOUT_MS = 1000
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io
|
||||
|
||||
enum class IOState {
|
||||
IDLE, WRITING, READING
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver;
|
||||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.packet
|
||||
|
||||
public interface OmnipodDashManager {
|
||||
}
|
||||
class BlePacket
|
|
@ -1,94 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan;
|
||||
|
||||
import android.bluetooth.le.ScanRecord;
|
||||
import android.bluetooth.le.ScanResult;
|
||||
import android.os.ParcelUuid;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.DiscoveredInvalidPodException;
|
||||
|
||||
public class BleDiscoveredDevice {
|
||||
private final ScanResult scanResult;
|
||||
private final long podID;
|
||||
private final int sequenceNo;
|
||||
private final long lotNo;
|
||||
|
||||
public BleDiscoveredDevice(ScanResult scanResult, long searchPodID)
|
||||
throws DiscoveredInvalidPodException {
|
||||
|
||||
this.scanResult = scanResult;
|
||||
this.podID = searchPodID;
|
||||
|
||||
this.validateServiceUUIDs();
|
||||
this.validatePodID();
|
||||
this.lotNo = this.parseLotNo();
|
||||
this.sequenceNo = this.parseSeqNo();
|
||||
}
|
||||
|
||||
private static String extractUUID16(ParcelUuid uuid) {
|
||||
return uuid.toString().substring(4, 8);
|
||||
}
|
||||
|
||||
private void validateServiceUUIDs()
|
||||
throws DiscoveredInvalidPodException {
|
||||
ScanRecord scanRecord = scanResult.getScanRecord();
|
||||
List<ParcelUuid> serviceUUIDs = scanRecord.getServiceUuids();
|
||||
|
||||
if (serviceUUIDs.size() != 9) {
|
||||
throw new DiscoveredInvalidPodException("Expected 9 service UUIDs, got" + serviceUUIDs.size(), serviceUUIDs);
|
||||
}
|
||||
if (!extractUUID16(serviceUUIDs.get(0)).equals("4024")) {
|
||||
// this is the service that we filtered for
|
||||
throw new DiscoveredInvalidPodException("The first exposed service UUID should be 4024, got " + extractUUID16(serviceUUIDs.get(0)), serviceUUIDs);
|
||||
}
|
||||
// TODO understand what is serviceUUIDs[1]. 0x2470. Alarms?
|
||||
if (!extractUUID16(serviceUUIDs.get(2)).equals("000a")) {
|
||||
// constant?
|
||||
throw new DiscoveredInvalidPodException("The third exposed service UUID should be 000a, got " + serviceUUIDs.get(2), serviceUUIDs);
|
||||
}
|
||||
}
|
||||
|
||||
private void validatePodID()
|
||||
throws DiscoveredInvalidPodException {
|
||||
ScanRecord scanRecord = scanResult.getScanRecord();
|
||||
List<ParcelUuid> serviceUUIDs = scanRecord.getServiceUuids();
|
||||
String hexPodID = extractUUID16(serviceUUIDs.get(3)) + extractUUID16(serviceUUIDs.get(4));
|
||||
Long podID = Long.parseLong(hexPodID, 16);
|
||||
if (this.podID != podID) {
|
||||
throw new DiscoveredInvalidPodException("This is not the POD we are looking for. " + this.podID + " found: " + podID, serviceUUIDs);
|
||||
}
|
||||
}
|
||||
|
||||
private long parseLotNo() {
|
||||
ScanRecord scanRecord = scanResult.getScanRecord();
|
||||
List<ParcelUuid> serviceUUIDs = scanRecord.getServiceUuids();
|
||||
String lotSeq = extractUUID16(serviceUUIDs.get(5)) +
|
||||
extractUUID16(serviceUUIDs.get(6)) +
|
||||
extractUUID16(serviceUUIDs.get(7));
|
||||
|
||||
return Long.parseLong(lotSeq.substring(0, 10), 16);
|
||||
}
|
||||
|
||||
private int parseSeqNo() {
|
||||
ScanRecord scanRecord = scanResult.getScanRecord();
|
||||
List<ParcelUuid> serviceUUIDs = scanRecord.getServiceUuids();
|
||||
String lotSeq = extractUUID16(serviceUUIDs.get(7)) +
|
||||
extractUUID16(serviceUUIDs.get(8));
|
||||
|
||||
return Integer.parseInt(lotSeq.substring(2), 16);
|
||||
}
|
||||
|
||||
public ScanResult getScanResult() {
|
||||
return this.scanResult;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "BleDiscoveredDevice{" +
|
||||
"scanResult=" + scanResult +
|
||||
", podID=" + podID +
|
||||
", sequenceNo=" + sequenceNo +
|
||||
", lotNo=" + lotNo +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan
|
||||
|
||||
import android.bluetooth.le.ScanResult
|
||||
import android.os.ParcelUuid
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.DiscoveredInvalidPodException
|
||||
|
||||
class BleDiscoveredDevice(val scanResult: ScanResult, private val podId: Long) {
|
||||
|
||||
private val sequenceNo: Int
|
||||
private val lotNo: Long
|
||||
@Throws(DiscoveredInvalidPodException::class)
|
||||
private fun validateServiceUUIDs() {
|
||||
val scanRecord = scanResult.scanRecord
|
||||
?: throw DiscoveredInvalidPodException("Scan record is null");
|
||||
val serviceUuids = scanRecord.serviceUuids
|
||||
if (serviceUuids.size != 9) {
|
||||
throw DiscoveredInvalidPodException("Expected 9 service UUIDs, got" + serviceUuids.size, serviceUuids)
|
||||
}
|
||||
if (extractUUID16(serviceUuids[0]) != MAIN_SERVICE_UUID) {
|
||||
// this is the service that we filtered for
|
||||
throw DiscoveredInvalidPodException("The first exposed service UUID should be 4024, got " + extractUUID16(serviceUuids[0]), serviceUuids)
|
||||
}
|
||||
// TODO understand what is serviceUUIDs[1]. 0x2470. Alarms?
|
||||
if (extractUUID16(serviceUuids[2]) != "000a") {
|
||||
// constant?
|
||||
throw DiscoveredInvalidPodException("The third exposed service UUID should be 000a, got " + serviceUuids[2], serviceUuids)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(DiscoveredInvalidPodException::class)
|
||||
private fun validatePodId() {
|
||||
val scanRecord = scanResult.scanRecord
|
||||
val serviceUUIDs = scanRecord.serviceUuids
|
||||
val hexPodId = extractUUID16(serviceUUIDs[3]) + extractUUID16(serviceUUIDs[4])
|
||||
val podId = hexPodId.toLong(16)
|
||||
if (this.podId != podId) {
|
||||
throw DiscoveredInvalidPodException("This is not the POD we are looking for. " + this.podId + " found: " + this.podId, serviceUUIDs)
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseLotNo(): Long {
|
||||
val scanRecord = scanResult.scanRecord
|
||||
val serviceUUIDs = scanRecord.serviceUuids
|
||||
val lotSeq = extractUUID16(serviceUUIDs[5]) +
|
||||
extractUUID16(serviceUUIDs[6]) +
|
||||
extractUUID16(serviceUUIDs[7])
|
||||
return lotSeq.substring(0, 10).toLong(16)
|
||||
}
|
||||
|
||||
private fun parseSeqNo(): Int {
|
||||
val scanRecord = scanResult.scanRecord
|
||||
val serviceUUIDs = scanRecord.serviceUuids
|
||||
val lotSeq = extractUUID16(serviceUUIDs[7]) +
|
||||
extractUUID16(serviceUUIDs[8])
|
||||
return lotSeq.substring(2).toInt(16)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "BleDiscoveredDevice{" +
|
||||
"scanResult=" + scanResult +
|
||||
", podID=" + podId +
|
||||
", sequenceNo=" + sequenceNo +
|
||||
", lotNo=" + lotNo +
|
||||
'}'
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MAIN_SERVICE_UUID = "4024";
|
||||
private fun extractUUID16(uuid: ParcelUuid): String {
|
||||
return uuid.toString().substring(4, 8)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
validateServiceUUIDs()
|
||||
validatePodId()
|
||||
lotNo = parseLotNo()
|
||||
sequenceNo = parseSeqNo()
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.le.BluetoothLeScanner;
|
||||
import android.bluetooth.le.ScanFilter;
|
||||
import android.bluetooth.le.ScanSettings;
|
||||
import android.os.ParcelUuid;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ScanFailException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ScanFailFoundTooManyException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ScanFailNotFoundException;
|
||||
|
||||
public class PodScanner {
|
||||
public static final String SCAN_FOR_SERVICE_UUID = "00004024-0000-1000-8000-00805F9B34FB";
|
||||
public static final long POD_ID_NOT_ACTIVATED = 4294967294L;
|
||||
private static final int SCAN_DURATION_MS = 5000;
|
||||
|
||||
private final BluetoothAdapter bluetoothAdapter;
|
||||
private final AAPSLogger logger;
|
||||
|
||||
public PodScanner(AAPSLogger logger, BluetoothAdapter bluetoothAdapter) {
|
||||
this.bluetoothAdapter = bluetoothAdapter;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public BleDiscoveredDevice scanForPod(String serviceUUID, long podID)
|
||||
throws InterruptedException, ScanFailException {
|
||||
BluetoothLeScanner scanner = this.bluetoothAdapter.getBluetoothLeScanner();
|
||||
|
||||
ScanFilter filter = new ScanFilter.Builder()
|
||||
.setServiceUuid(ParcelUuid.fromString(serviceUUID))
|
||||
.build();
|
||||
|
||||
ScanSettings scanSettings = new ScanSettings.Builder()
|
||||
.setLegacy(false)
|
||||
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
|
||||
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
|
||||
.build();
|
||||
|
||||
ScanCollector scanCollector = new ScanCollector(this.logger, podID);
|
||||
this.logger.debug(LTag.PUMPBTCOMM, "Scanning with filters: "+ filter.toString() + " settings" + scanSettings.toString());
|
||||
scanner.startScan(Arrays.asList(filter), scanSettings, scanCollector);
|
||||
|
||||
Thread.sleep(SCAN_DURATION_MS);
|
||||
|
||||
scanner.flushPendingScanResults(scanCollector);
|
||||
scanner.stopScan(scanCollector);
|
||||
|
||||
List<BleDiscoveredDevice> collected = scanCollector.collect();
|
||||
if (collected.size() == 0) {
|
||||
throw new ScanFailNotFoundException();
|
||||
} else if (collected.size() > 1) {
|
||||
throw new ScanFailFoundTooManyException(collected);
|
||||
}
|
||||
return collected.get(0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.le.ScanFilter
|
||||
import android.bluetooth.le.ScanSettings
|
||||
import android.os.ParcelUuid
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ScanFailException
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ScanFailFoundTooManyException
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ScanFailNotFoundException
|
||||
import java.util.*
|
||||
|
||||
class PodScanner(private val logger: AAPSLogger, private val bluetoothAdapter: BluetoothAdapter) {
|
||||
|
||||
@Throws(InterruptedException::class, ScanFailException::class)
|
||||
fun scanForPod(serviceUUID: String?, podID: Long): BleDiscoveredDevice {
|
||||
val scanner = bluetoothAdapter.bluetoothLeScanner
|
||||
val filter = ScanFilter.Builder()
|
||||
.setServiceUuid(ParcelUuid.fromString(serviceUUID))
|
||||
.build()
|
||||
val scanSettings = ScanSettings.Builder()
|
||||
.setLegacy(false)
|
||||
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
|
||||
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
|
||||
.build()
|
||||
val scanCollector = ScanCollector(logger, podID)
|
||||
logger.debug(LTag.PUMPBTCOMM, "Scanning with filters: $filter settings$scanSettings")
|
||||
scanner.startScan(Arrays.asList(filter), scanSettings, scanCollector)
|
||||
Thread.sleep(SCAN_DURATION_MS.toLong())
|
||||
scanner.flushPendingScanResults(scanCollector)
|
||||
scanner.stopScan(scanCollector)
|
||||
val collected = scanCollector.collect()
|
||||
if (collected.isEmpty()) {
|
||||
throw ScanFailNotFoundException()
|
||||
} else if (collected.size > 1) {
|
||||
throw ScanFailFoundTooManyException(collected)
|
||||
}
|
||||
return collected[0]
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val SCAN_FOR_SERVICE_UUID = "00004024-0000-1000-8000-00805F9B34FB"
|
||||
const val POD_ID_NOT_ACTIVATED = 4294967294L
|
||||
private const val SCAN_DURATION_MS = 5000
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan;
|
||||
|
||||
import android.bluetooth.le.ScanCallback;
|
||||
import android.bluetooth.le.ScanResult;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.DiscoveredInvalidPodException;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ScanFailException;
|
||||
|
||||
public class ScanCollector extends ScanCallback {
|
||||
private final AAPSLogger logger;
|
||||
private final long podID;
|
||||
// there could be different threads calling the onScanResult callback
|
||||
private final ConcurrentHashMap<String, ScanResult> found;
|
||||
private int scanFailed;
|
||||
|
||||
public ScanCollector(AAPSLogger logger, long podID) {
|
||||
this.podID = podID;
|
||||
this.logger = logger;
|
||||
this.found = new ConcurrentHashMap<String, ScanResult>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScanResult(int callbackType, ScanResult result) {
|
||||
// callbackType will be ALL
|
||||
this.logger.debug(LTag.PUMPBTCOMM, "Scan found: "+result.toString());
|
||||
this.found.put(result.getDevice().getAddress(), result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScanFailed(int errorCode) {
|
||||
this.scanFailed = errorCode;
|
||||
this.logger.warn(LTag.PUMPBTCOMM, "Scan failed with errorCode: "+errorCode);
|
||||
super.onScanFailed(errorCode);
|
||||
}
|
||||
|
||||
public List<BleDiscoveredDevice> collect()
|
||||
throws ScanFailException {
|
||||
List<BleDiscoveredDevice> ret = new ArrayList<>();
|
||||
|
||||
if (this.scanFailed != 0) {
|
||||
throw new ScanFailException(this.scanFailed);
|
||||
}
|
||||
|
||||
logger.debug(LTag.PUMPBTCOMM, "ScanCollector looking for podID: " + this.podID);
|
||||
|
||||
for (ScanResult result : this.found.values()) {
|
||||
try {
|
||||
BleDiscoveredDevice device = new BleDiscoveredDevice(result, this.podID);
|
||||
ret.add(device);
|
||||
logger.debug(LTag.PUMPBTCOMM, "ScanCollector found: " + result.toString() + "Pod ID: " + this.podID);
|
||||
} catch (DiscoveredInvalidPodException e) {
|
||||
logger.debug(LTag.PUMPBTCOMM, "ScanCollector: pod not matching" + e.toString());
|
||||
// this is not the POD we are looking for
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(ret);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan
|
||||
|
||||
import android.bluetooth.le.ScanCallback
|
||||
import android.bluetooth.le.ScanResult
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.DiscoveredInvalidPodException
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ScanFailException
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class ScanCollector(private val logger: AAPSLogger, private val podID: Long) : ScanCallback() {
|
||||
|
||||
// there could be different threads calling the onScanResult callback
|
||||
private val found: ConcurrentHashMap<String, ScanResult> = ConcurrentHashMap()
|
||||
private var scanFailed = 0
|
||||
override fun onScanResult(callbackType: Int, result: ScanResult) {
|
||||
// callbackType will be ALL
|
||||
logger.debug(LTag.PUMPBTCOMM, "Scan found: $result")
|
||||
found[result.device.address] = result
|
||||
}
|
||||
|
||||
override fun onScanFailed(errorCode: Int) {
|
||||
logger.warn(LTag.PUMPBTCOMM, "Scan failed with errorCode: $errorCode")
|
||||
super.onScanFailed(errorCode)
|
||||
}
|
||||
|
||||
@Throws(ScanFailException::class) fun collect(): List<BleDiscoveredDevice> {
|
||||
val ret: MutableList<BleDiscoveredDevice> = ArrayList()
|
||||
if (scanFailed != 0) {
|
||||
throw ScanFailException(scanFailed)
|
||||
}
|
||||
logger.debug(LTag.PUMPBTCOMM, "ScanCollector looking for podID: $podID")
|
||||
for (result in found.values) {
|
||||
try {
|
||||
val device = BleDiscoveredDevice(result, podID)
|
||||
ret.add(device)
|
||||
logger.debug(LTag.PUMPBTCOMM, "ScanCollector found: " + result.toString() + "Pod ID: " + podID)
|
||||
} catch (e: DiscoveredInvalidPodException) {
|
||||
logger.debug(LTag.PUMPBTCOMM, "ScanCollector: pod not matching$e")
|
||||
// this is not the POD we are looking for
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(ret)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event
|
||||
|
||||
class PodEvent(
|
||||
val type: PodEventType,
|
||||
val data: Any?
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event
|
||||
|
||||
enum class PodEventType {
|
||||
SCANNING,
|
||||
PAIRING,
|
||||
CONNECTING,
|
||||
CONNECTED,
|
||||
COMMAND_SENDING,
|
||||
COMMAND_SENT,
|
||||
RESPONSE_RECEIVED,
|
||||
// ...
|
||||
}
|
|
@ -5,13 +5,13 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.b
|
|||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class DeactivateCommand internal constructor(uniqueId: Int, sequenceNumber: Short, multiCommandFlag: Boolean, nonce: Int) : NonceEnabledCommand(CommandType.DEACTIVATE, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
|
||||
class DeactivateCommand internal constructor(
|
||||
uniqueId: Int,
|
||||
sequenceNumber: Short,
|
||||
multiCommandFlag: Boolean,
|
||||
nonce: Int
|
||||
) : NonceEnabledCommand(CommandType.DEACTIVATE, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
override val encoded: ByteArray
|
||||
get() = appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
|
|
|
@ -5,16 +5,15 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.b
|
|||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.HeaderEnabledCommandBuilder
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class GetVersionCommand internal constructor(uniqueId: Int, sequenceNumber: Short, multiCommandFlag: Boolean) : HeaderEnabledCommand(CommandType.GET_VERSION, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||
class GetVersionCommand internal constructor(
|
||||
uniqueId: Int,
|
||||
sequenceNumber: Short,
|
||||
multiCommandFlag: Boolean
|
||||
) : HeaderEnabledCommand(CommandType.GET_VERSION, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
override val encoded: ByteArray
|
||||
get() = HeaderEnabledCommand.Companion.appendCrc(ByteBuffer.allocate(LENGTH + HeaderEnabledCommand.Companion.HEADER_LENGTH) //
|
||||
.put(HeaderEnabledCommand.Companion.encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
get() = appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.putInt(uniqueId) //
|
||||
|
|
|
@ -7,7 +7,13 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definitio
|
|||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
|
||||
class ProgramAlertsCommand internal constructor(uniqueId: Int, sequenceNumber: Short, multiCommandFlag: Boolean, alertConfigurations: List<AlertConfiguration>?, nonce: Int) : NonceEnabledCommand(CommandType.PROGRAM_ALERTS, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
|
||||
class ProgramAlertsCommand internal constructor(
|
||||
uniqueId: Int,
|
||||
sequenceNumber: Short,
|
||||
multiCommandFlag: Boolean,
|
||||
alertConfigurations: List<AlertConfiguration>?,
|
||||
nonce: Int
|
||||
) : NonceEnabledCommand(CommandType.PROGRAM_ALERTS, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
|
||||
|
||||
private val alertConfigurations: List<AlertConfiguration>
|
||||
|
||||
|
|
|
@ -12,7 +12,17 @@ import java.nio.ByteBuffer
|
|||
import java.util.*
|
||||
|
||||
// Always preceded by 0x1a ProgramInsulinCommand
|
||||
class ProgramBasalCommand internal constructor(private val interlockCommand: ProgramInsulinCommand, uniqueId: Int, sequenceNumber: Short, multiCommandFlag: Boolean, insulinProgramElements: List<BasalInsulinProgramElement>?, programReminder: ProgramReminder, currentInsulinProgramElementIndex: Byte, remainingTenthPulsesInCurrentInsulinProgramElement: Short, delayUntilNextTenthPulseInUsec: Int) : HeaderEnabledCommand(CommandType.PROGRAM_BASAL, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||
class ProgramBasalCommand internal constructor(
|
||||
private val interlockCommand: ProgramInsulinCommand,
|
||||
uniqueId: Int,
|
||||
sequenceNumber: Short,
|
||||
multiCommandFlag: Boolean,
|
||||
insulinProgramElements: List<BasalInsulinProgramElement>,
|
||||
programReminder: ProgramReminder,
|
||||
currentInsulinProgramElementIndex: Byte,
|
||||
remainingTenthPulsesInCurrentInsulinProgramElement: Short,
|
||||
delayUntilNextTenthPulseInUsec: Int
|
||||
) : HeaderEnabledCommand(CommandType.PROGRAM_BASAL, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||
|
||||
private val insulinProgramElements: List<BasalInsulinProgramElement>
|
||||
private val programReminder: ProgramReminder
|
||||
|
@ -22,17 +32,8 @@ class ProgramBasalCommand internal constructor(private val interlockCommand: Pro
|
|||
val length: Short
|
||||
get() = (insulinProgramElements.size * 6 + 10).toShort()
|
||||
val bodyLength: Byte
|
||||
get() = (insulinProgramElements.size * 6 + 8).toByte()//
|
||||
get() = (insulinProgramElements.size * 6 + 8).toByte()
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
override val encoded: ByteArray
|
||||
get() {
|
||||
val buffer = ByteBuffer.allocate(length.toInt()) //
|
||||
|
@ -47,8 +48,8 @@ class ProgramBasalCommand internal constructor(private val interlockCommand: Pro
|
|||
}
|
||||
val basalCommand = buffer.array()
|
||||
val interlockCommand = interlockCommand.encoded
|
||||
val header: ByteArray = HeaderEnabledCommand.Companion.encodeHeader(uniqueId, sequenceNumber, (basalCommand.size + interlockCommand!!.size).toShort(), multiCommandFlag)
|
||||
return HeaderEnabledCommand.appendCrc(ByteBuffer.allocate(basalCommand.size + interlockCommand.size + header.size) //
|
||||
val header: ByteArray = encodeHeader(uniqueId, sequenceNumber, (basalCommand.size + interlockCommand.size).toShort(), multiCommandFlag)
|
||||
return appendCrc(ByteBuffer.allocate(basalCommand.size + interlockCommand.size + header.size) //
|
||||
.put(header) //
|
||||
.put(interlockCommand) //
|
||||
.put(basalCommand) //
|
||||
|
|
|
@ -9,20 +9,16 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.Mess
|
|||
import java.nio.ByteBuffer
|
||||
|
||||
// NOT SUPPORTED: extended bolus
|
||||
class ProgramBolusCommand internal constructor(private val interlockCommand: ProgramInsulinCommand, uniqueId: Int, sequenceNumber: Short, multiCommandFlag: Boolean, private val programReminder: ProgramReminder, private val numberOfTenthPulses: Short, //
|
||||
private val delayUntilFirstTenthPulseInUsec: Int) : HeaderEnabledCommand(CommandType.PROGRAM_BOLUS, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||
class ProgramBolusCommand internal constructor(
|
||||
private val interlockCommand: ProgramInsulinCommand,
|
||||
uniqueId: Int,
|
||||
sequenceNumber: Short,
|
||||
multiCommandFlag: Boolean,
|
||||
private val programReminder: ProgramReminder,
|
||||
private val numberOfTenthPulses: Short,
|
||||
private val delayUntilFirstTenthPulseInUsec: Int
|
||||
) : HeaderEnabledCommand(CommandType.PROGRAM_BOLUS, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// Extended bolus pulses
|
||||
// Delay between tenth extended pulses in usec
|
||||
override val encoded: ByteArray
|
||||
get() {
|
||||
val bolusCommand = ByteBuffer.allocate(LENGTH.toInt()) //
|
||||
|
@ -35,8 +31,8 @@ class ProgramBolusCommand internal constructor(private val interlockCommand: Pro
|
|||
.putInt(0) // Delay between tenth extended pulses in usec
|
||||
.array()
|
||||
val interlockCommand = interlockCommand.encoded
|
||||
val header: ByteArray = HeaderEnabledCommand.Companion.encodeHeader(uniqueId, sequenceNumber, (bolusCommand.size + interlockCommand!!.size).toShort(), multiCommandFlag)
|
||||
return HeaderEnabledCommand.Companion.appendCrc(ByteBuffer.allocate(header.size + interlockCommand.size + bolusCommand.size) //
|
||||
val header: ByteArray = encodeHeader(uniqueId, sequenceNumber, (bolusCommand.size + interlockCommand.size).toShort(), multiCommandFlag)
|
||||
return appendCrc(ByteBuffer.allocate(header.size + interlockCommand.size + bolusCommand.size) //
|
||||
.put(header) //
|
||||
.put(interlockCommand) //
|
||||
.put(bolusCommand) //
|
||||
|
|
|
@ -7,7 +7,18 @@ import java.nio.ByteBuffer
|
|||
import java.util.*
|
||||
|
||||
// Always followed by one of: 0x13, 0x16, 0x17
|
||||
class ProgramInsulinCommand(uniqueId: Int, sequenceNumber: Short, multiCommandFlag: Boolean, nonce: Int, insulinProgramElements: List<ShortInsulinProgramElement>, private val checksum: Short, private val byte9: Byte, private val byte10And11: Short, private val byte12And13: Short, private val deliveryType: DeliveryType) : NonceEnabledCommand(CommandType.PROGRAM_INSULIN, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
|
||||
class ProgramInsulinCommand(
|
||||
uniqueId: Int,
|
||||
sequenceNumber: Short,
|
||||
multiCommandFlag: Boolean,
|
||||
nonce: Int, insulinProgramElements:
|
||||
List<ShortInsulinProgramElement>,
|
||||
private val checksum: Short,
|
||||
private val byte9: Byte,
|
||||
private val byte10And11: Short,
|
||||
private val byte12And13: Short,
|
||||
private val deliveryType: DeliveryType
|
||||
) : NonceEnabledCommand(CommandType.PROGRAM_INSULIN, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
|
||||
|
||||
private val insulinProgramElements: List<ShortInsulinProgramElement> = ArrayList(insulinProgramElements)
|
||||
|
||||
|
|
|
@ -11,8 +11,14 @@ import java.nio.ByteBuffer
|
|||
import java.util.*
|
||||
|
||||
// NOT SUPPORTED: percentage temp basal
|
||||
class ProgramTempBasalCommand protected constructor(private val interlockCommand: ProgramInsulinCommand, uniqueId: Int, sequenceNumber: Short, multiCommandFlag: Boolean,
|
||||
private val programReminder: ProgramReminder, insulinProgramElements: List<BasalInsulinProgramElement>) : HeaderEnabledCommand(CommandType.PROGRAM_TEMP_BASAL, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||
class ProgramTempBasalCommand protected constructor(
|
||||
private val interlockCommand: ProgramInsulinCommand,
|
||||
uniqueId: Int,
|
||||
sequenceNumber: Short,
|
||||
multiCommandFlag: Boolean,
|
||||
private val programReminder: ProgramReminder,
|
||||
insulinProgramElements: List<BasalInsulinProgramElement>
|
||||
) : HeaderEnabledCommand(CommandType.PROGRAM_TEMP_BASAL, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||
|
||||
private val insulinProgramElements: List<BasalInsulinProgramElement>
|
||||
fun getBodyLength(): Byte = (insulinProgramElements.size * 6 + 8).toByte()
|
||||
|
@ -84,8 +90,8 @@ class ProgramTempBasalCommand protected constructor(private val interlockCommand
|
|||
}
|
||||
val tempBasalCommand = buffer.array()
|
||||
val interlockCommand = interlockCommand.encoded
|
||||
val header: ByteArray = HeaderEnabledCommand.Companion.encodeHeader(uniqueId, sequenceNumber, (tempBasalCommand.size + interlockCommand!!.size).toShort(), multiCommandFlag)
|
||||
return HeaderEnabledCommand.Companion.appendCrc(ByteBuffer.allocate(header.size + interlockCommand.size + tempBasalCommand.size) //
|
||||
val header: ByteArray = encodeHeader(uniqueId, sequenceNumber, (tempBasalCommand.size + interlockCommand!!.size).toShort(), multiCommandFlag)
|
||||
return appendCrc(ByteBuffer.allocate(header.size + interlockCommand.size + tempBasalCommand.size) //
|
||||
.put(header) //
|
||||
.put(interlockCommand) //
|
||||
.put(tempBasalCommand) //
|
||||
|
|
|
@ -6,21 +6,18 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.b
|
|||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
|
||||
class SetUniqueIdCommand internal constructor(uniqueId: Int, sequenceNumber: Short, multiCommandFlag: Boolean, private val lotNumber: Int, private val podSequenceNumber: Int, private val initializationTime: Date) : HeaderEnabledCommand(CommandType.SET_UNIQUE_ID, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||
class SetUniqueIdCommand internal constructor(
|
||||
uniqueId: Int,
|
||||
sequenceNumber: Short,
|
||||
multiCommandFlag: Boolean,
|
||||
private val lotNumber: Int,
|
||||
private val podSequenceNumber: Int,
|
||||
private val initializationTime: Date
|
||||
) : HeaderEnabledCommand(CommandType.SET_UNIQUE_ID, uniqueId, sequenceNumber, multiCommandFlag) {
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// FIXME ??
|
||||
// FIXME ??
|
||||
//
|
||||
//
|
||||
//
|
||||
override val encoded: ByteArray
|
||||
get() = HeaderEnabledCommand.Companion.appendCrc(ByteBuffer.allocate(LENGTH + HeaderEnabledCommand.Companion.HEADER_LENGTH) //
|
||||
.put(HeaderEnabledCommand.Companion.encodeHeader(DEFAULT_UNIQUE_ID, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
get() = appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(DEFAULT_UNIQUE_ID, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.putInt(uniqueId) //
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.HeaderEnabledCommand
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
|
||||
class SilenceAlertsCommand internal constructor(uniqueId: Int, sequenceNumber: Short, multiCommandFlag: Boolean, private val parameters: SilenceAlertCommandParameters, nonce: Int) : NonceEnabledCommand(CommandType.SILENCE_ALERTS, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
|
||||
class SilenceAlertsCommand internal constructor(
|
||||
uniqueId: Int,
|
||||
sequenceNumber: Short,
|
||||
multiCommandFlag: Boolean,
|
||||
private val parameters: SilenceAlertCommandParameters,
|
||||
nonce: Int
|
||||
) : NonceEnabledCommand(CommandType.SILENCE_ALERTS, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
|
||||
|
||||
override val encoded: ByteArray
|
||||
get() =
|
||||
HeaderEnabledCommand.Companion.appendCrc(ByteBuffer.allocate(LENGTH + HeaderEnabledCommand.Companion.HEADER_LENGTH) //
|
||||
.put(HeaderEnabledCommand.Companion.encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.putInt(nonce) //
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.HeaderEnabledCommand
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType
|
||||
|
@ -9,12 +8,19 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definitio
|
|||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
|
||||
class StopDeliveryCommand internal constructor(uniqueId: Int, sequenceNumber: Short, multiCommandFlag: Boolean, private val deliveryType: DeliveryType, private val beepType: BeepType, nonce: Int) : NonceEnabledCommand(CommandType.STOP_DELIVERY, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
|
||||
class StopDeliveryCommand internal constructor(
|
||||
uniqueId: Int,
|
||||
sequenceNumber: Short,
|
||||
multiCommandFlag: Boolean,
|
||||
private val deliveryType: DeliveryType,
|
||||
private val beepType: BeepType,
|
||||
nonce: Int
|
||||
) : NonceEnabledCommand(CommandType.STOP_DELIVERY, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
|
||||
|
||||
override val encoded: ByteArray
|
||||
get() {
|
||||
return HeaderEnabledCommand.Companion.appendCrc(ByteBuffer.allocate(LENGTH + HeaderEnabledCommand.Companion.HEADER_LENGTH) //
|
||||
.put(HeaderEnabledCommand.Companion.encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
|
||||
.put(commandType.value) //
|
||||
.put(BODY_LENGTH) //
|
||||
.putInt(nonce) //
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable
|
||||
import java.io.Serializable
|
||||
|
||||
interface Command : Encodable {
|
||||
interface Command : Encodable, Serializable {
|
||||
|
||||
val commandType: CommandType
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base
|
||||
|
||||
enum class CommandType(var value: Byte) {
|
||||
enum class CommandType(
|
||||
val value: Byte
|
||||
) {
|
||||
|
||||
SET_UNIQUE_ID(0x03.toByte()),
|
||||
GET_VERSION(0x07.toByte()),
|
||||
GET_STATUS(0x0e.toByte()),
|
||||
|
|
|
@ -2,13 +2,15 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.
|
|||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.util.ProgramBasalUtil
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable
|
||||
import java.io.Serializable
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
open class BasalInsulinProgramElement(val startSlotIndex: Byte, val numberOfSlots: Byte, val totalTenthPulses: Short) : Encodable {
|
||||
open class BasalInsulinProgramElement(
|
||||
val startSlotIndex: Byte,
|
||||
val numberOfSlots: Byte,
|
||||
val totalTenthPulses: Short
|
||||
) : Encodable, Serializable {
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
override val encoded: ByteArray
|
||||
get() = ByteBuffer.allocate(6) //
|
||||
.putShort(totalTenthPulses) //
|
||||
|
|
|
@ -3,15 +3,12 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.
|
|||
import java.nio.ByteBuffer
|
||||
import kotlin.experimental.and
|
||||
|
||||
class BasalShortInsulinProgramElement(// 4 bits
|
||||
private val numberOfSlots: Byte, // 10 bits
|
||||
private val pulsesPerSlot: Short, //
|
||||
private val extraAlternatePulse: Boolean) : ShortInsulinProgramElement {
|
||||
class BasalShortInsulinProgramElement(
|
||||
private val numberOfSlots: Byte, // 4 bits
|
||||
private val pulsesPerSlot: Short, //10 bits
|
||||
private val extraAlternatePulse: Boolean
|
||||
) : ShortInsulinProgramElement {
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
override val encoded: ByteArray
|
||||
get() {
|
||||
val firstByte = (numberOfSlots - 1 and 0x0f shl 4 //
|
||||
|
|
|
@ -2,7 +2,9 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.
|
|||
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class BolusShortInsulinProgramElement(private val numberOfPulses: Short) : ShortInsulinProgramElement {
|
||||
class BolusShortInsulinProgramElement(
|
||||
private val numberOfPulses: Short
|
||||
) : ShortInsulinProgramElement {
|
||||
|
||||
override val encoded: ByteArray
|
||||
get() = ByteBuffer.allocate(2).putShort(numberOfPulses).array()
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program
|
||||
|
||||
class CurrentBasalInsulinProgramElement(val index: Byte, val delayUntilNextTenthPulseInUsec: Int, val remainingTenthPulses: Short) {
|
||||
import java.io.Serializable
|
||||
|
||||
class CurrentBasalInsulinProgramElement(
|
||||
val index: Byte,
|
||||
val delayUntilNextTenthPulseInUsec: Int,
|
||||
val remainingTenthPulses: Short
|
||||
) : Serializable {
|
||||
|
||||
override fun toString(): String = "CurrentLongInsulinProgramElement{" +
|
||||
"index=" + index +
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program
|
||||
|
||||
class CurrentSlot(val index: Byte, val eighthSecondsRemaining: Short, val pulsesRemaining: Short) {
|
||||
import java.io.Serializable
|
||||
|
||||
class CurrentSlot(
|
||||
val index: Byte,
|
||||
val eighthSecondsRemaining: Short,
|
||||
val pulsesRemaining: Short
|
||||
) : Serializable {
|
||||
|
||||
override fun toString(): String = "CurrentSlot{" +
|
||||
"index=" + index +
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable
|
||||
import java.io.Serializable
|
||||
|
||||
interface ShortInsulinProgramElement : Encodable
|
||||
interface ShortInsulinProgramElement : Encodable, Serializable
|
|
@ -2,10 +2,12 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.
|
|||
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class TempBasalInsulinProgramElement(startSlotIndex: Byte, numberOfSlots: Byte, totalTenthPulses: Short) : BasalInsulinProgramElement(startSlotIndex, numberOfSlots, totalTenthPulses) {
|
||||
class TempBasalInsulinProgramElement(
|
||||
startSlotIndex: Byte,
|
||||
numberOfSlots: Byte,
|
||||
totalTenthPulses: Short
|
||||
) : BasalInsulinProgramElement(startSlotIndex, numberOfSlots, totalTenthPulses) {
|
||||
|
||||
//
|
||||
//
|
||||
override val encoded: ByteArray
|
||||
get() {
|
||||
val buffer = ByteBuffer.allocate(6)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
|
||||
|
||||
enum class ActivationProgress {
|
||||
NOT_STARTED,
|
||||
GOT_POD_VERSION,
|
||||
SET_UNIQUE_ID,
|
||||
PROGRAMMED_LOW_RESERVOIR_ALERTS,
|
||||
REPROGRAMMED_LUMP_OF_COAL_ALERT,
|
||||
PRIMING,
|
||||
PRIME_COMPLETED,
|
||||
PROGRAMMED_USER_SET_EXPIRATION_ALERT,
|
||||
PHASE_1_COMPLETED,
|
||||
PROGRAMMED_BASAL,
|
||||
PROGRAMMED_CANCEL_LOC_ETC_ALERT,
|
||||
INSERTING_CANNULA,
|
||||
CANNULA_INSERTED,
|
||||
COMPLETED;
|
||||
|
||||
fun isBefore(other: ActivationProgress): Boolean = ordinal < other.ordinal
|
||||
|
||||
fun isAtLeast(other: ActivationProgress): Boolean = ordinal >= other.ordinal
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -3,8 +3,16 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definiti
|
|||
import java.nio.ByteBuffer
|
||||
import kotlin.experimental.or
|
||||
|
||||
class AlertConfiguration(private val slot: AlertSlot, private val enabled: Boolean, private val durationInMinutes: Short, private val autoOff: Boolean, private val triggerType: AlertTriggerType, private val offsetInMinutesOrThresholdInMicroLiters: Short, private val beepType: BeepType, private val beepRepetition: BeepRepetitionType) : Encodable {
|
||||
|
||||
class AlertConfiguration(
|
||||
private val slot: AlertSlot,
|
||||
private val enabled: Boolean,
|
||||
private val durationInMinutes: Short,
|
||||
private val autoOff: Boolean,
|
||||
private val triggerType: AlertTriggerType,
|
||||
private val offsetInMinutesOrThresholdInMicroLiters: Short,
|
||||
private val beepType: BeepType,
|
||||
private val beepRepetition: BeepRepetitionType
|
||||
) : Encodable {
|
||||
|
||||
override val encoded: ByteArray
|
||||
get() {
|
||||
|
|
|
@ -1,7 +1,18 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
|
||||
|
||||
enum class AlertSlot(val value: Byte) {
|
||||
AUTO_OFF(0x00.toByte()), MULTI_COMMAND(0x01.toByte()), EXPIRATION_IMMINENT(0x02.toByte()), USER_SET_EXPIRATION(0x03.toByte()), LOW_RESERVOIR(0x04.toByte()), SUSPEND_IN_PROGRESS(0x05.toByte()), SUSPEND_ENDED(0x06.toByte()), EXPIRATION(0x07.toByte()), UNKNOWN(0xff.toByte());
|
||||
enum class AlertSlot(
|
||||
val value: Byte
|
||||
) {
|
||||
|
||||
AUTO_OFF(0x00.toByte()),
|
||||
MULTI_COMMAND(0x01.toByte()),
|
||||
EXPIRATION_IMMINENT(0x02.toByte()),
|
||||
USER_SET_EXPIRATION(0x03.toByte()),
|
||||
LOW_RESERVOIR(0x04.toByte()),
|
||||
SUSPEND_IN_PROGRESS(0x05.toByte()),
|
||||
SUSPEND_ENDED(0x06.toByte()),
|
||||
EXPIRATION(0x07.toByte()),
|
||||
UNKNOWN(0xff.toByte());
|
||||
|
||||
companion object {
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
|
||||
|
||||
enum class AlertTriggerType {
|
||||
TIME_TRIGGER, RESERVOIR_VOLUME_TRIGGER
|
||||
TIME_TRIGGER,
|
||||
RESERVOIR_VOLUME_TRIGGER
|
||||
}
|
|
@ -2,7 +2,9 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definiti
|
|||
|
||||
import java.util.*
|
||||
|
||||
class BasalProgram(segments: List<Segment>) {
|
||||
class BasalProgram(
|
||||
segments: List<Segment>
|
||||
) {
|
||||
|
||||
val segments: MutableList<Segment> = segments.toMutableList()
|
||||
get() = Collections.unmodifiableList(field) // TODO Adrian: moved method here
|
||||
|
@ -15,6 +17,8 @@ class BasalProgram(segments: List<Segment>) {
|
|||
|
||||
fun isZeroBasal() = segments.sumBy(Segment::basalRateInHundredthUnitsPerHour) == 0
|
||||
|
||||
fun rateAt(date: Date): Double = 0.0 // TODO
|
||||
|
||||
class Segment(val startSlotIndex: Short, val endSlotIndex: Short, val basalRateInHundredthUnitsPerHour: Int) {
|
||||
|
||||
fun getPulsesPerHour(): Short {
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
|
||||
|
||||
// FIXME names
|
||||
enum class BeepRepetitionType( // Used in lump of coal alert
|
||||
val value: Byte) {
|
||||
enum class BeepRepetitionType(
|
||||
val value: Byte
|
||||
) {
|
||||
|
||||
XXX(0x01.toByte()), // Used in low reservoir alert
|
||||
XXX2(0x03.toByte()), // Used in user pod expiration alert
|
||||
XXX3(0x05.toByte()), // Used in pod expiration alert
|
||||
XXX4(0x06.toByte()), // Used in imminent pod expiration alert
|
||||
XXX5(0x08.toByte());
|
||||
XXX(0x01.toByte()), // Used in lump of coal alert
|
||||
XXX2(0x03.toByte()), // Used in low reservoir alert
|
||||
XXX3(0x05.toByte()), // Used in user pod expiration alert
|
||||
XXX4(0x06.toByte()), // Used in pod expiration alert
|
||||
XXX5(0x08.toByte()); // Used in imminent pod expiration alert
|
||||
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
|
||||
|
||||
enum class BeepType( // Used in stop delivery command
|
||||
val value: Byte) {
|
||||
val value: Byte
|
||||
) {
|
||||
|
||||
SILENT(0x00.toByte()), FOUR_TIMES_BIP_BEEP(0x02.toByte()), // Used in low reservoir alert, user expiration alert, expiration alert, imminent expiration alert, lump of coal alert
|
||||
SILENT(0x00.toByte()),
|
||||
FOUR_TIMES_BIP_BEEP(0x02.toByte()), // Used in low reservoir alert, user expiration alert, expiration alert, imminent expiration alert, lump of coal alert
|
||||
LONG_SINGLE_BEEP(0x06.toByte());
|
||||
}
|
|
@ -1,7 +1,16 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
|
||||
|
||||
enum class DeliveryStatus(private val value: Byte) {
|
||||
SUSPENDED(0x00.toByte()), BASAL_ACTIVE(0x01.toByte()), TEMP_BASAL_ACTIVE(0x02.toByte()), PRIMING(0x04.toByte()), BOLUS_AND_BASAL_ACTIVE(0x05.toByte()), BOLUS_AND_TEMP_BASAL_ACTIVE(0x06.toByte()), UNKNOWN(0xff.toByte());
|
||||
enum class DeliveryStatus(
|
||||
private val value: Byte
|
||||
) {
|
||||
|
||||
SUSPENDED(0x00.toByte()),
|
||||
BASAL_ACTIVE(0x01.toByte()),
|
||||
TEMP_BASAL_ACTIVE(0x02.toByte()),
|
||||
PRIMING(0x04.toByte()),
|
||||
BOLUS_AND_BASAL_ACTIVE(0x05.toByte()),
|
||||
BOLUS_AND_TEMP_BASAL_ACTIVE(0x06.toByte()),
|
||||
UNKNOWN(0xff.toByte());
|
||||
|
||||
companion object {
|
||||
|
||||
|
|
|
@ -1,7 +1,39 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
|
||||
|
||||
enum class NakErrorType(private val value: Byte) {
|
||||
FLASH_WRITE(0x01.toByte()), FLASH_ERASE(0x02.toByte()), FLASH_OPERATION(0x03.toByte()), FLASH_ADDR(0x04.toByte()), POD_STATE(0x05.toByte()), CRITICAL_VARIABLE(0x06.toByte()), ILLEGAL_PARAM(0x07.toByte()), BOLUS_CRITICAL_VAR(0x08.toByte()), INT_ILLEGAL_PARAM(0x09.toByte()), ILLEGAL_CHECKSUM(0x0a.toByte()), INVALID_MSG_LEN(0x0b.toByte()), PUMP_STATE(0x0c.toByte()), ILLEGAL_COMMAND(0x0d.toByte()), ILLEGAL_FILL_STATE(0x0e.toByte()), MAX_READWRITE_SIZE(0x0f.toByte()), ILLEGAL_READ_ADDRESS(0x10.toByte()), ILLEGAL_READ_MEM_TYPE(0x11.toByte()), INIT_POD(0x12.toByte()), ILLEGAL_CMD_STATE(0x13.toByte()), ILLEGAL_SECURITY_CODE(0x14.toByte()), POD_IN_ALARM(0x15.toByte()), COMD_NOT_SET(0x16.toByte()), ILLEGAL_RX_SENS_VALUE(0x17.toByte()), ILLEGAL_TX_PKT_SIZE(0x18.toByte()), OCCL_PARAMS_ALREADY_SET(0x19.toByte()), OCCL_PARAM(0x1a.toByte()), ILLEGAL_CDTHR_VALUE(0x1b.toByte()), IGNORE_COMMAND(0x1c.toByte()), INVALID_CRC(0x1d.toByte()), UNKNOWN(0xff.toByte());
|
||||
enum class NakErrorType(
|
||||
private val value: Byte
|
||||
) {
|
||||
|
||||
FLASH_WRITE(0x01.toByte()),
|
||||
FLASH_ERASE(0x02.toByte()),
|
||||
FLASH_OPERATION(0x03.toByte()),
|
||||
FLASH_ADDR(0x04.toByte()),
|
||||
POD_STATE(0x05.toByte()),
|
||||
CRITICAL_VARIABLE(0x06.toByte()),
|
||||
ILLEGAL_PARAM(0x07.toByte()),
|
||||
BOLUS_CRITICAL_VAR(0x08.toByte()),
|
||||
INT_ILLEGAL_PARAM(0x09.toByte()),
|
||||
ILLEGAL_CHECKSUM(0x0a.toByte()),
|
||||
INVALID_MSG_LEN(0x0b.toByte()),
|
||||
PUMP_STATE(0x0c.toByte()),
|
||||
ILLEGAL_COMMAND(0x0d.toByte()),
|
||||
ILLEGAL_FILL_STATE(0x0e.toByte()),
|
||||
MAX_READWRITE_SIZE(0x0f.toByte()),
|
||||
ILLEGAL_READ_ADDRESS(0x10.toByte()),
|
||||
ILLEGAL_READ_MEM_TYPE(0x11.toByte()),
|
||||
INIT_POD(0x12.toByte()),
|
||||
ILLEGAL_CMD_STATE(0x13.toByte()),
|
||||
ILLEGAL_SECURITY_CODE(0x14.toByte()),
|
||||
POD_IN_ALARM(0x15.toByte()),
|
||||
COMD_NOT_SET(0x16.toByte()),
|
||||
ILLEGAL_RX_SENS_VALUE(0x17.toByte()),
|
||||
ILLEGAL_TX_PKT_SIZE(0x18.toByte()),
|
||||
OCCL_PARAMS_ALREADY_SET(0x19.toByte()),
|
||||
OCCL_PARAM(0x1a.toByte()),
|
||||
ILLEGAL_CDTHR_VALUE(0x1b.toByte()),
|
||||
IGNORE_COMMAND(0x1c.toByte()),
|
||||
INVALID_CRC(0x1d.toByte()),
|
||||
UNKNOWN(0xff.toByte());
|
||||
|
||||
companion object {
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
|
||||
|
||||
class OmnipodEvent {
|
||||
enum class OmnipodEventType {
|
||||
CONNECTED, ALREADY_CONNECTED, FAILED_TO_CONNECT, DISCONNECTED, COMMAND_SENT, GOT_POD_VERSION, SET_UNIQUE_ID, PRIMED_PUMP, FINISHED_ACTIVATION_1, PROGRAMMED_BASAL, PROGRAMMED_ALERTS, SET_BEEPS, INSERTED_CANNULA, FINISHED_ACTIVATION_2, PROGRAMMED_TEMP_BASAL, STARTED_BOLUS, STOPPED_DELIVERY, SILENCED_ALERTS, DEACTIVATED, COMMAND_SENDING
|
||||
}
|
||||
}
|
|
@ -1,7 +1,26 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
|
||||
|
||||
enum class PodStatus(private val value: Byte) {
|
||||
UNINITIALIZED(0x00.toByte()), MFG_TEST(0x01.toByte()), FILLED(0x02.toByte()), UID_SET(0x03.toByte()), ENGAGING_CLUTCH_DRIVE(0x04.toByte()), CLUTCH_DRIVE_ENGAGED(0x05.toByte()), BASAL_PROGRAM_RUNNING(0x06.toByte()), PRIMING(0x07.toByte()), RUNNING_ABOVE_MIN_VOLUME(0x08.toByte()), RUNNING_BELOW_MIN_VOLUME(0x09.toByte()), UNUSED_10(0x0a.toByte()), UNUSED_11(0x0b.toByte()), UNUSED_12(0x0c.toByte()), ALARM(0x0d.toByte()), LUMP_OF_COAL(0x0e.toByte()), DEACTIVATED(0x0f.toByte()), UNKNOWN(0xff.toByte());
|
||||
enum class PodStatus(
|
||||
private val value: Byte
|
||||
) {
|
||||
|
||||
UNINITIALIZED(0x00.toByte()),
|
||||
MFG_TEST(0x01.toByte()),
|
||||
FILLED(0x02.toByte()),
|
||||
UID_SET(0x03.toByte()),
|
||||
ENGAGING_CLUTCH_DRIVE(0x04.toByte()),
|
||||
CLUTCH_DRIVE_ENGAGED(0x05.toByte()),
|
||||
BASAL_PROGRAM_SET(0x06.toByte()),
|
||||
PRIMING(0x07.toByte()),
|
||||
RUNNING_ABOVE_MIN_VOLUME(0x08.toByte()),
|
||||
RUNNING_BELOW_MIN_VOLUME(0x09.toByte()),
|
||||
UNUSED_10(0x0a.toByte()),
|
||||
UNUSED_11(0x0b.toByte()),
|
||||
UNUSED_12(0x0c.toByte()),
|
||||
ALARM(0x0d.toByte()),
|
||||
LUMP_OF_COAL(0x0e.toByte()),
|
||||
DEACTIVATED(0x0f.toByte()),
|
||||
UNKNOWN(0xff.toByte());
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -14,4 +33,6 @@ enum class PodStatus(private val value: Byte) {
|
|||
return UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
fun isRunning(): Boolean = this == RUNNING_ABOVE_MIN_VOLUME || this == RUNNING_BELOW_MIN_VOLUME
|
||||
}
|
|
@ -1,8 +1,13 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
|
||||
|
||||
import java.io.Serializable
|
||||
import kotlin.experimental.and
|
||||
|
||||
class ProgramReminder(private val atStart: Boolean, private val atEnd: Boolean, private val atInterval: Byte) : Encodable {
|
||||
class ProgramReminder(
|
||||
private val atStart: Boolean,
|
||||
private val atEnd: Boolean,
|
||||
private val atInterval: Byte
|
||||
) : Encodable, Serializable {
|
||||
|
||||
override val encoded: ByteArray
|
||||
get() = byteArrayOf(((if (atStart) 1 else 0) shl 7
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
data class SoftwareVersion(
|
||||
private val major: Short,
|
||||
private val minor: Short,
|
||||
private val interim: Short
|
||||
) : Serializable {
|
||||
|
||||
override fun toString(): String {
|
||||
return "$major.$minor.$interim"
|
||||
}
|
||||
}
|
|
@ -2,4 +2,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response
|
|||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.ResponseType.ActivationResponseType
|
||||
|
||||
abstract class ActivationResponseBase(val activationResponseType: ActivationResponseType, encoded: ByteArray) : ResponseBase(ResponseType.ACTIVATION_RESPONSE, encoded)
|
||||
abstract class ActivationResponseBase(
|
||||
val activationResponseType: ActivationResponseType,
|
||||
encoded: ByteArray
|
||||
) : ResponseBase(ResponseType.ACTIVATION_RESPONSE, encoded)
|
|
@ -2,4 +2,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response
|
|||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.ResponseType.AdditionalStatusResponseType
|
||||
|
||||
open class AdditionalStatusResponseBase internal constructor(val statusResponseType: AdditionalStatusResponseType, encoded: ByteArray) : ResponseBase(ResponseType.ADDITIONAL_STATUS_RESPONSE, encoded)
|
||||
open class AdditionalStatusResponseBase internal constructor(
|
||||
val statusResponseType: AdditionalStatusResponseType,
|
||||
encoded: ByteArray
|
||||
) : ResponseBase(ResponseType.ADDITIONAL_STATUS_RESPONSE, encoded)
|
|
@ -1,12 +1,16 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.*
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlarmType
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.DeliveryStatus
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.ResponseType.AdditionalStatusResponseType
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
import kotlin.experimental.and
|
||||
|
||||
class AlarmStatusResponse(encoded: ByteArray) : AdditionalStatusResponseBase(AdditionalStatusResponseType.ALARM_STATUS, encoded) {
|
||||
class AlarmStatusResponse(
|
||||
encoded: ByteArray
|
||||
) : AdditionalStatusResponseBase(AdditionalStatusResponseType.ALARM_STATUS, encoded) {
|
||||
|
||||
private val messageType: Byte
|
||||
private val messageLength: Short
|
||||
|
@ -38,6 +42,7 @@ class AlarmStatusResponse(encoded: ByteArray) : AdditionalStatusResponseBase(Add
|
|||
private val receiverLowerGain: Short
|
||||
private val podStatusWhenAlarmOccurred2: PodStatus
|
||||
private val returnAddressOfPodAlarmHandlerCaller: Short
|
||||
|
||||
fun getMessageType(): Byte {
|
||||
return messageType
|
||||
}
|
||||
|
@ -201,11 +206,11 @@ class AlarmStatusResponse(encoded: ByteArray) : AdditionalStatusResponseBase(Add
|
|||
messageLength = (encoded[1].toInt() and 0xff).toShort()
|
||||
additionalStatusResponseType = encoded[2]
|
||||
podStatus = PodStatus.byValue((encoded[3] and 0x0f))
|
||||
deliveryStatus = DeliveryStatus.Companion.byValue((encoded[4] and 0x0f))
|
||||
deliveryStatus = DeliveryStatus.byValue((encoded[4] and 0x0f))
|
||||
bolusPulsesRemaining = (ByteBuffer.wrap(byteArrayOf(encoded[5], encoded[6])).short and 2047)
|
||||
sequenceNumberOfLastProgrammingCommand = (encoded[7] and 0x0f).toShort()
|
||||
totalPulsesDelivered = ByteBuffer.wrap(byteArrayOf(encoded[8], encoded[9])).short
|
||||
alarmType = AlarmType.Companion.byValue(encoded[10])
|
||||
alarmType = AlarmType.byValue(encoded[10])
|
||||
alarmTime = ByteBuffer.wrap(byteArrayOf(encoded[11], encoded[12])).short
|
||||
reservoirPulsesRemaining = ByteBuffer.wrap(byteArrayOf(encoded[13], encoded[14])).short
|
||||
minutesSinceActivation = ByteBuffer.wrap(byteArrayOf(encoded[15], encoded[16])).short
|
||||
|
|
|
@ -4,12 +4,14 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definitio
|
|||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus
|
||||
import kotlin.experimental.and
|
||||
|
||||
class DefaultStatusResponse(encoded: ByteArray) : ResponseBase(ResponseType.DEFAULT_STATUS_RESPONSE, encoded) {
|
||||
class DefaultStatusResponse(
|
||||
encoded: ByteArray
|
||||
) : ResponseBase(ResponseType.DEFAULT_STATUS_RESPONSE, encoded) {
|
||||
|
||||
// TODO: Here is a lot of bitshifting that had to be changed. we should go over it.
|
||||
private val messageType: Byte = encoded[0]
|
||||
private val deliveryStatus: DeliveryStatus = DeliveryStatus.byValue((encoded[1].toInt() shr 4 and 0x0f).toByte())
|
||||
private val podStatus: PodStatus = PodStatus.Companion.byValue((encoded[1] and 0x0f) as Byte)
|
||||
private val podStatus: PodStatus = PodStatus.byValue((encoded[1] and 0x0f) as Byte)
|
||||
private val totalPulsesDelivered: Short = ((encoded[2] and 0x0f shl 12 or (encoded[3].toInt() and 0xff shl 1) or (encoded[4].toInt() and 0xff ushr 7)).toShort())
|
||||
private val sequenceNumberOfLastProgrammingCommand: Short = (encoded[4] ushr 3 and 0x0f).toShort()
|
||||
private val bolusPulsesRemaining: Short = ((encoded[4] and 0x07 shl 10 or (encoded[5].toInt() and 0xff) and 2047).toShort())
|
||||
|
|
|
@ -3,9 +3,10 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response
|
|||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlarmType
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.NakErrorType
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus
|
||||
import java.util.*
|
||||
|
||||
class NakResponse(encoded: ByteArray) : ResponseBase(ResponseType.NAK_RESPONSE, encoded) {
|
||||
class NakResponse(
|
||||
encoded: ByteArray
|
||||
) : ResponseBase(ResponseType.NAK_RESPONSE, encoded) {
|
||||
|
||||
private val messageType: Byte // TODO directly assign here
|
||||
private val messageLength: Short
|
||||
|
@ -53,7 +54,7 @@ class NakResponse(encoded: ByteArray) : ResponseBase(ResponseType.NAK_RESPONSE,
|
|||
init {
|
||||
messageType = encoded[0]
|
||||
messageLength = encoded[1].toShort()
|
||||
nakErrorType = NakErrorType.Companion.byValue(encoded[2])
|
||||
nakErrorType = NakErrorType.byValue(encoded[2])
|
||||
val byte3 = encoded[3]
|
||||
val byte4 = encoded[4]
|
||||
if (nakErrorType == NakErrorType.ILLEGAL_SECURITY_CODE) {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response
|
||||
|
||||
interface Response {
|
||||
import java.io.Serializable
|
||||
|
||||
interface Response : Serializable {
|
||||
|
||||
val responseType: ResponseType
|
||||
val encoded: ByteArray
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response
|
||||
|
||||
abstract class ResponseBase(override val responseType: ResponseType, encoded: ByteArray) : Response {
|
||||
abstract class ResponseBase(
|
||||
override val responseType: ResponseType,
|
||||
encoded: ByteArray
|
||||
) : Response {
|
||||
|
||||
override val encoded: ByteArray = encoded.copyOf(encoded.size)
|
||||
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response
|
||||
|
||||
enum class ResponseType(private val value: Byte) {
|
||||
ACTIVATION_RESPONSE(0x01.toByte()), DEFAULT_STATUS_RESPONSE(0x1d.toByte()), ADDITIONAL_STATUS_RESPONSE(0x02.toByte()), NAK_RESPONSE(0x06.toByte()), UNKNOWN(0xff.toByte());
|
||||
enum class ResponseType(
|
||||
private val value: Byte
|
||||
) {
|
||||
|
||||
ACTIVATION_RESPONSE(0x01.toByte()),
|
||||
DEFAULT_STATUS_RESPONSE(0x1d.toByte()),
|
||||
ADDITIONAL_STATUS_RESPONSE(0x02.toByte()),
|
||||
NAK_RESPONSE(0x06.toByte()),
|
||||
UNKNOWN(0xff.toByte());
|
||||
|
||||
fun getValue(): Byte {
|
||||
return value
|
||||
|
|
|
@ -3,10 +3,10 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response
|
|||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.ResponseType.ActivationResponseType
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
import kotlin.experimental.and
|
||||
|
||||
class SetUniqueIdResponse(encoded: ByteArray) : ActivationResponseBase(ActivationResponseType.SET_UNIQUE_ID_RESPONSE, encoded) {
|
||||
class SetUniqueIdResponse(
|
||||
encoded: ByteArray
|
||||
) : ActivationResponseBase(ActivationResponseType.SET_UNIQUE_ID_RESPONSE, encoded) {
|
||||
|
||||
private val messageType: Byte // TODO directly assign here
|
||||
private val messageLength: Short
|
||||
|
@ -27,6 +27,7 @@ class SetUniqueIdResponse(encoded: ByteArray) : ActivationResponseBase(Activatio
|
|||
private val lotNumber: Long
|
||||
private val podSequenceNumber: Long
|
||||
private val uniqueIdReceivedInCommand: Long
|
||||
|
||||
fun getMessageType(): Byte {
|
||||
return messageType
|
||||
}
|
||||
|
@ -132,20 +133,20 @@ class SetUniqueIdResponse(encoded: ByteArray) : ActivationResponseBase(Activatio
|
|||
|
||||
init {
|
||||
messageType = encoded[0]
|
||||
messageLength = (encoded[1].toInt() and 0xff) .toShort()
|
||||
messageLength = (encoded[1].toInt() and 0xff).toShort()
|
||||
pulseVolumeInTenThousandthMicroLiter = ByteBuffer.wrap(byteArrayOf(encoded[2], encoded[3])).short
|
||||
pumpRate = (encoded[4].toInt() and 0xff) .toShort()
|
||||
primePumpRate = (encoded[5].toInt() and 0xff) .toShort()
|
||||
numberOfEngagingClutchDrivePulses = (encoded[6].toInt() and 0xff) .toShort()
|
||||
numberOfPrimePulses = (encoded[7].toInt() and 0xff) .toShort()
|
||||
podExpirationTimeInHours = (encoded[8].toInt() and 0xff) .toShort()
|
||||
firmwareVersionMajor = (encoded[9].toInt() and 0xff) .toShort()
|
||||
firmwareVersionMinor = (encoded[10].toInt() and 0xff) .toShort()
|
||||
firmwareVersionInterim = (encoded[11].toInt() and 0xff) .toShort()
|
||||
bleVersionMajor = (encoded[12].toInt() and 0xff) .toShort()
|
||||
bleVersionMinor = (encoded[13].toInt() and 0xff) .toShort()
|
||||
bleVersionInterim = (encoded[14].toInt() and 0xff) .toShort()
|
||||
productId = (encoded[15].toInt() and 0xff) .toShort()
|
||||
pumpRate = (encoded[4].toInt() and 0xff).toShort()
|
||||
primePumpRate = (encoded[5].toInt() and 0xff).toShort()
|
||||
numberOfEngagingClutchDrivePulses = (encoded[6].toInt() and 0xff).toShort()
|
||||
numberOfPrimePulses = (encoded[7].toInt() and 0xff).toShort()
|
||||
podExpirationTimeInHours = (encoded[8].toInt() and 0xff).toShort()
|
||||
firmwareVersionMajor = (encoded[9].toInt() and 0xff).toShort()
|
||||
firmwareVersionMinor = (encoded[10].toInt() and 0xff).toShort()
|
||||
firmwareVersionInterim = (encoded[11].toInt() and 0xff).toShort()
|
||||
bleVersionMajor = (encoded[12].toInt() and 0xff).toShort()
|
||||
bleVersionMinor = (encoded[13].toInt() and 0xff).toShort()
|
||||
bleVersionInterim = (encoded[14].toInt() and 0xff).toShort()
|
||||
productId = (encoded[15].toInt() and 0xff).toShort()
|
||||
podStatus = PodStatus.byValue(encoded[16])
|
||||
lotNumber = ByteBuffer.wrap(byteArrayOf(0, 0, 0, 0, encoded[17], encoded[18], encoded[19], encoded[20])).long
|
||||
podSequenceNumber = ByteBuffer.wrap(byteArrayOf(0, 0, 0, 0, encoded[21], encoded[22], encoded[23], encoded[24])).long
|
||||
|
|
|
@ -6,23 +6,26 @@ import java.nio.ByteBuffer
|
|||
import java.util.*
|
||||
import kotlin.experimental.and
|
||||
|
||||
class VersionResponse(encoded: ByteArray) : ActivationResponseBase(ActivationResponseType.GET_VERSION_RESPONSE, encoded) {
|
||||
class VersionResponse(
|
||||
encoded: ByteArray
|
||||
) : ActivationResponseBase(ActivationResponseType.GET_VERSION_RESPONSE, encoded) {
|
||||
|
||||
private val messageType: Byte = encoded[0]
|
||||
private val messageLength: Short = (encoded[1].toInt() and 0xff) .toShort()
|
||||
private val firmwareVersionMajor: Short = (encoded[2].toInt() and 0xff) .toShort()
|
||||
private val firmwareVersionMinor: Short = (encoded[3].toInt() and 0xff) .toShort()
|
||||
private val firmwareVersionInterim: Short = (encoded[4].toInt() and 0xff) .toShort()
|
||||
private val bleVersionMajor: Short = (encoded[5].toInt() and 0xff) .toShort()
|
||||
private val bleVersionMinor: Short = (encoded[6].toInt() and 0xff) .toShort()
|
||||
private val bleVersionInterim: Short = (encoded[7].toInt() and 0xff) .toShort()
|
||||
private val productId: Short = (encoded[8].toInt() and 0xff) .toShort()
|
||||
private val messageLength: Short = (encoded[1].toInt() and 0xff).toShort()
|
||||
private val firmwareVersionMajor: Short = (encoded[2].toInt() and 0xff).toShort()
|
||||
private val firmwareVersionMinor: Short = (encoded[3].toInt() and 0xff).toShort()
|
||||
private val firmwareVersionInterim: Short = (encoded[4].toInt() and 0xff).toShort()
|
||||
private val bleVersionMajor: Short = (encoded[5].toInt() and 0xff).toShort()
|
||||
private val bleVersionMinor: Short = (encoded[6].toInt() and 0xff).toShort()
|
||||
private val bleVersionInterim: Short = (encoded[7].toInt() and 0xff).toShort()
|
||||
private val productId: Short = (encoded[8].toInt() and 0xff).toShort()
|
||||
private val podStatus: PodStatus = PodStatus.byValue((encoded[9] and 0xf))
|
||||
private val lotNumber: Long = ByteBuffer.wrap(byteArrayOf(0, 0, 0, 0, encoded[10], encoded[11], encoded[12], encoded[13])).long
|
||||
private val podSequenceNumber: Long = ByteBuffer.wrap(byteArrayOf(0, 0, 0, 0, encoded[14], encoded[15], encoded[16], encoded[17])).long
|
||||
private val rssi: Byte = (encoded[18] and 0x3f)
|
||||
private val receiverLowerGain: Byte = ((encoded[18].toInt() shr 6 and 0x03).toByte())
|
||||
private val uniqueIdReceivedInCommand: Long = ByteBuffer.wrap(byteArrayOf(0, 0, 0, 0, encoded[19], encoded[20], encoded[21], encoded[22])).long
|
||||
|
||||
fun getMessageType(): Byte {
|
||||
return messageType
|
||||
}
|
||||
|
|
|
@ -1,3 +1,56 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state
|
||||
|
||||
interface OmnipodDashPodStateManager
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.*
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.AlarmStatusResponse
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.DefaultStatusResponse
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.SetUniqueIdResponse
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse
|
||||
import java.io.Serializable
|
||||
import java.util.*
|
||||
|
||||
interface OmnipodDashPodStateManager {
|
||||
|
||||
var activationProgress: ActivationProgress
|
||||
val isUniqueIdSet: Boolean
|
||||
val isActivationCompleted: Boolean
|
||||
val isSuspended: Boolean
|
||||
val isPodRunning: Boolean
|
||||
var lastConnection: Long
|
||||
val lastUpdated: Long
|
||||
|
||||
val messageSequenceNumber: Short
|
||||
val sequenceNumberOfLastProgrammingCommand: Short?
|
||||
val activationTime: Long?
|
||||
val uniqueId: Long?
|
||||
val bluetoothAddress: String?
|
||||
|
||||
val bluetoothVersion: SoftwareVersion?
|
||||
val firmwareVersion: SoftwareVersion?
|
||||
val lotNumber: Long?
|
||||
val podSequenceNumber: Long?
|
||||
val pulseRate: Short?
|
||||
val primePulseRate: Short?
|
||||
val podLifeInHours: Short?
|
||||
val firstPrimeBolusVolume: Short?
|
||||
val secondPrimeBolusVolume: Short?
|
||||
|
||||
val pulsesDelivered: Short?
|
||||
val pulsesRemaining: Short?
|
||||
val podStatus: PodStatus?
|
||||
val deliveryStatus: DeliveryStatus?
|
||||
val minutesSinceActivation: Short?
|
||||
val activeAlerts: EnumSet<AlertSlot>?
|
||||
|
||||
val tempBasal: TempBasal?
|
||||
val tempBasalActive: Boolean
|
||||
val basalProgram: BasalProgram?
|
||||
|
||||
fun increaseMessageSequenceNumber()
|
||||
fun updateFromDefaultStatusResponse(response: DefaultStatusResponse)
|
||||
fun updateFromVersionResponse(response: VersionResponse)
|
||||
fun updateFromSetUniqueIdResponse(response: SetUniqueIdResponse)
|
||||
fun updateFromAlarmStatusResponse(response: AlarmStatusResponse)
|
||||
fun reset()
|
||||
|
||||
data class TempBasal(val startTime: Long, val rate: Double, val durationInMinutes: Short) : Serializable
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state
|
||||
|
||||
import com.google.gson.Gson
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.R
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.*
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.AlarmStatusResponse
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.DefaultStatusResponse
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.SetUniqueIdResponse
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import java.io.Serializable
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||
private val logger: AAPSLogger,
|
||||
private val sharedPreferences: SP
|
||||
) : OmnipodDashPodStateManager {
|
||||
|
||||
private var podState: PodState
|
||||
|
||||
init {
|
||||
podState = load()
|
||||
}
|
||||
|
||||
override var activationProgress: ActivationProgress
|
||||
get() = podState.activationProgress
|
||||
set(value) {
|
||||
podState.activationProgress = value
|
||||
store()
|
||||
}
|
||||
|
||||
override val isUniqueIdSet: Boolean = activationProgress.isAtLeast(ActivationProgress.SET_UNIQUE_ID)
|
||||
|
||||
override val isActivationCompleted: Boolean = activationProgress == ActivationProgress.COMPLETED
|
||||
|
||||
override val isSuspended: Boolean = podState.deliveryStatus?.equals(DeliveryStatus.SUSPENDED)
|
||||
?: true
|
||||
|
||||
override val isPodRunning: Boolean = podState.podStatus?.isRunning() ?: false
|
||||
|
||||
override var lastConnection: Long
|
||||
get() = podState.lastConnection
|
||||
set(value) {
|
||||
podState.lastConnection = value
|
||||
store()
|
||||
}
|
||||
|
||||
override val lastUpdated: Long = podState.lastUpdated
|
||||
|
||||
override val messageSequenceNumber: Short = podState.messageSequenceNumber
|
||||
|
||||
override val sequenceNumberOfLastProgrammingCommand: Short? = podState.sequenceNumberOfLastProgrammingCommand
|
||||
|
||||
override val activationTime: Long? = podState.activationTime
|
||||
|
||||
override val uniqueId: Long? = podState.uniqueId
|
||||
|
||||
override val bluetoothAddress: String? = podState.bluetoothAddress
|
||||
|
||||
override val bluetoothVersion: SoftwareVersion? = podState.bleVersion
|
||||
|
||||
override val firmwareVersion: SoftwareVersion? = podState.firmwareVersion
|
||||
|
||||
override val lotNumber: Long? = podState.lotNumber
|
||||
|
||||
override val podSequenceNumber: Long? = podState.podSequenceNumber
|
||||
|
||||
override val pulseRate: Short? = podState.pulseRate
|
||||
|
||||
override val primePulseRate: Short? = podState.primePulseRate
|
||||
|
||||
override val podLifeInHours: Short? = podState.podLifeInHours
|
||||
|
||||
override val firstPrimeBolusVolume: Short? = podState.firstPrimeBolusVolume
|
||||
|
||||
override val secondPrimeBolusVolume: Short? = podState.secondPrimeBolusVolume
|
||||
|
||||
override val pulsesDelivered: Short? = podState.pulsesDelivered
|
||||
|
||||
override val pulsesRemaining: Short? = podState.pulsesRemaining
|
||||
|
||||
override val podStatus: PodStatus? = podState.podStatus
|
||||
|
||||
override val deliveryStatus: DeliveryStatus? = podState.deliveryStatus
|
||||
|
||||
override val minutesSinceActivation: Short? = podState.minutesSinceActivation
|
||||
|
||||
override val activeAlerts: EnumSet<AlertSlot>? = podState.activeAlerts
|
||||
|
||||
override val tempBasal: OmnipodDashPodStateManager.TempBasal? = podState.tempBasal
|
||||
|
||||
override val tempBasalActive: Boolean
|
||||
get() = tempBasal != null && tempBasal.startTime + tempBasal.durationInMinutes * 60 * 1000 > System.currentTimeMillis()
|
||||
|
||||
override val basalProgram: BasalProgram? = podState.basalProgram
|
||||
|
||||
override fun increaseMessageSequenceNumber() {
|
||||
podState.messageSequenceNumber = ((podState.messageSequenceNumber.toInt() + 1) and 0x0f).toShort()
|
||||
store()
|
||||
}
|
||||
|
||||
override fun updateFromDefaultStatusResponse(response: DefaultStatusResponse) {
|
||||
podState.deliveryStatus = response.getDeliveryStatus()
|
||||
podState.podStatus = response.getPodStatus()
|
||||
podState.pulsesDelivered = response.getTotalPulsesDelivered()
|
||||
podState.pulsesRemaining = response.getReservoirPulsesRemaining()
|
||||
podState.sequenceNumberOfLastProgrammingCommand = response.getSequenceNumberOfLastProgrammingCommand()
|
||||
podState.minutesSinceActivation = response.getMinutesSinceActivation()
|
||||
|
||||
// TODO active alerts
|
||||
|
||||
podState.lastUpdated = System.currentTimeMillis()
|
||||
store();
|
||||
}
|
||||
|
||||
override fun updateFromVersionResponse(response: VersionResponse) {
|
||||
podState.bleVersion = SoftwareVersion(response.getBleVersionMajor(), response.getBleVersionMinor(), response.getBleVersionInterim())
|
||||
podState.firmwareVersion = SoftwareVersion(response.getFirmwareVersionMajor(), response.getFirmwareVersionMinor(), response.getFirmwareVersionInterim())
|
||||
podState.podStatus = response.getPodStatus()
|
||||
podState.lotNumber = response.getLotNumber()
|
||||
podState.podSequenceNumber = response.getPodSequenceNumber()
|
||||
|
||||
podState.lastUpdated = System.currentTimeMillis()
|
||||
store()
|
||||
}
|
||||
|
||||
override fun updateFromSetUniqueIdResponse(response: SetUniqueIdResponse) {
|
||||
podState.pulseRate = response.getDeliveryRate()
|
||||
podState.primePulseRate = response.getPrimeRate()
|
||||
podState.firstPrimeBolusVolume = response.getNumberOfPrimePulses()
|
||||
podState.secondPrimeBolusVolume = response.getNumberOfEngagingClutchDrivePulses()
|
||||
podState.podLifeInHours = response.getPodExpirationTimeInHours()
|
||||
podState.bleVersion = SoftwareVersion(response.getBleVersionMajor(), response.getBleVersionMinor(), response.getBleVersionInterim())
|
||||
podState.firmwareVersion = SoftwareVersion(response.getFirmwareVersionMajor(), response.getFirmwareVersionMinor(), response.getFirmwareVersionInterim())
|
||||
podState.podStatus = response.getPodStatus()
|
||||
podState.lotNumber = response.getLotNumber()
|
||||
podState.podSequenceNumber = response.getPodSequenceNumber()
|
||||
podState.uniqueId = response.getUniqueIdReceivedInCommand()
|
||||
|
||||
podState.lastUpdated = System.currentTimeMillis()
|
||||
store()
|
||||
}
|
||||
|
||||
override fun updateFromAlarmStatusResponse(response: AlarmStatusResponse) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
podState = PodState()
|
||||
store()
|
||||
}
|
||||
|
||||
private fun store() {
|
||||
try {
|
||||
val serialized = Gson().toJson(podState)
|
||||
logger.debug(LTag.PUMP, "Storing Pod state: $serialized")
|
||||
sharedPreferences.putString(R.string.key_omnipod_dash_pod_state, serialized)
|
||||
} catch (ex: Exception) {
|
||||
logger.error(LTag.PUMP, "Failed to store Pod state", ex)
|
||||
}
|
||||
}
|
||||
|
||||
private fun load(): PodState {
|
||||
if (sharedPreferences.contains(R.string.key_omnipod_dash_pod_state)) {
|
||||
try {
|
||||
return Gson().fromJson(sharedPreferences.getString(R.string.key_omnipod_dash_pod_state, ""), PodState::class.java)
|
||||
} catch (ex: Exception) {
|
||||
logger.error(LTag.PUMP, "Failed to deserialize Pod state", ex)
|
||||
}
|
||||
}
|
||||
logger.debug(LTag.PUMP, "Creating new Pod state")
|
||||
return PodState()
|
||||
}
|
||||
|
||||
class PodState : Serializable {
|
||||
|
||||
var activationProgress: ActivationProgress = ActivationProgress.NOT_STARTED
|
||||
var lastConnection: Long = 0
|
||||
var lastUpdated: Long = 0
|
||||
|
||||
var messageSequenceNumber: Short = 0
|
||||
var sequenceNumberOfLastProgrammingCommand: Short? = null
|
||||
var activationTime: Long? = null
|
||||
var uniqueId: Long? = null
|
||||
var bluetoothAddress: String? = null
|
||||
|
||||
var bleVersion: SoftwareVersion? = null
|
||||
var firmwareVersion: SoftwareVersion? = null
|
||||
var lotNumber: Long? = null
|
||||
var podSequenceNumber: Long? = null
|
||||
var pulseRate: Short? = null
|
||||
var primePulseRate: Short? = null
|
||||
var podLifeInHours: Short? = null
|
||||
var firstPrimeBolusVolume: Short? = null
|
||||
var secondPrimeBolusVolume: Short? = null
|
||||
|
||||
var pulsesDelivered: Short? = null
|
||||
var pulsesRemaining: Short? = null
|
||||
var podStatus: PodStatus? = null
|
||||
var deliveryStatus: DeliveryStatus? = null
|
||||
var minutesSinceActivation: Short? = null
|
||||
var activeAlerts: EnumSet<AlertSlot>? = null
|
||||
|
||||
var basalProgram: BasalProgram? = null
|
||||
var tempBasal: OmnipodDashPodStateManager.TempBasal? = null
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue