Implement ble-scanning for a new pod
This commit is contained in:
parent
12ff37123d
commit
fa8fdd9816
19 changed files with 550 additions and 2 deletions
|
@ -4,6 +4,7 @@ import dagger.Module
|
||||||
import dagger.android.ContributesAndroidInjector
|
import dagger.android.ContributesAndroidInjector
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.common.dagger.ActivityScope
|
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.common.dagger.OmnipodWizardModule
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.BleManager
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.DashPodManagementActivity
|
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.OmnipodDashOverviewFragment
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.wizard.activation.DashPodActivationWizardActivity
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.wizard.activation.DashPodActivationWizardActivity
|
||||||
|
@ -30,4 +31,6 @@ abstract class OmnipodDashModule {
|
||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector
|
||||||
abstract fun contributesOmnipodDashOverviewFragment(): OmnipodDashOverviewFragment
|
abstract fun contributesOmnipodDashOverviewFragment(): OmnipodDashOverviewFragment
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract fun contributesBleManager(): BleManager
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothGattCallback;
|
||||||
|
|
||||||
|
public class BleCommCallbacks extends BluetoothGattCallback {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,159 @@
|
||||||
|
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.blecommand;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
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,7 @@
|
||||||
|
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,4 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||||
|
|
||||||
|
public class CouldNotSendBleCmdException extends CouldNotSendBleException {
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||||
|
|
||||||
|
public class CouldNotSendBleException extends Exception {
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
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,11 @@
|
||||||
|
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,10 @@
|
||||||
|
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,20 @@
|
||||||
|
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,4 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions;
|
||||||
|
|
||||||
|
public class ScanFailNotFoundException extends ScanFailException {
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
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,94 @@
|
||||||
|
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,62 @@
|
||||||
|
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,66 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.wizard.activation.viewmodel.action
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.wizard.activation.viewmodel.action
|
||||||
|
|
||||||
|
import android.os.AsyncTask
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.android.HasAndroidInjector
|
import dagger.android.HasAndroidInjector
|
||||||
import info.nightscout.androidaps.data.PumpEnactResult
|
import info.nightscout.androidaps.data.PumpEnactResult
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
import info.nightscout.androidaps.logging.LTag
|
import info.nightscout.androidaps.logging.LTag
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.action.InitializePodViewModel
|
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.action.InitializePodViewModel
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.R
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.R
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.BleManager
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class DashInitializePodViewModel @Inject constructor(private val aapsLogger: AAPSLogger, private val injector: HasAndroidInjector) : InitializePodViewModel() {
|
class DashInitializePodViewModel @Inject constructor(private val aapsLogger: AAPSLogger,
|
||||||
|
private val injector: HasAndroidInjector,
|
||||||
|
private val bleManager: BleManager) : InitializePodViewModel() {
|
||||||
|
|
||||||
override fun isPodInAlarm(): Boolean = false // TODO
|
override fun isPodInAlarm(): Boolean = false // TODO
|
||||||
|
|
||||||
|
@ -19,7 +25,14 @@ class DashInitializePodViewModel @Inject constructor(private val aapsLogger: AAP
|
||||||
|
|
||||||
override fun doExecuteAction(): PumpEnactResult {
|
override fun doExecuteAction(): PumpEnactResult {
|
||||||
// TODO FIRST STEP OF ACTIVATION
|
// TODO FIRST STEP OF ACTIVATION
|
||||||
aapsLogger.debug(LTag.PUMP, "started activation part 1")
|
AsyncTask.execute {
|
||||||
|
try {
|
||||||
|
bleManager.activateNewPod()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
aapsLogger.error(LTag.PUMP, "TEST ACTIVATE Exception" + e.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return PumpEnactResult(injector).success(false).comment("not implemented")
|
return PumpEnactResult(injector).success(false).comment("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue