aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrygve Laugstøl <trygvis@inamo.no>2015-01-04 23:48:53 +0100
committerTrygve Laugstøl <trygvis@inamo.no>2015-01-04 23:48:53 +0100
commit4a2ca2d94c827566f8682e8dbd6fbdf17d70b4dd (patch)
tree10c9fe3e9b3c829cfc675d3184ce122964441fb5
parent31fc64bec1c5286c27bdc1f683d037ae0e91418d (diff)
downloadio.trygvis.soilmoisture-android-4a2ca2d94c827566f8682e8dbd6fbdf17d70b4dd.tar.gz
io.trygvis.soilmoisture-android-4a2ca2d94c827566f8682e8dbd6fbdf17d70b4dd.tar.bz2
io.trygvis.soilmoisture-android-4a2ca2d94c827566f8682e8dbd6fbdf17d70b4dd.tar.xz
io.trygvis.soilmoisture-android-4a2ca2d94c827566f8682e8dbd6fbdf17d70b4dd.zip
o Adding a way to prepend callbacks on the promise's queue. Needed for devices that give you [disconnect, connect] events when connecting. Yay.
o Reading meta data from the Soil Moisture device.
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtCallback.java20
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtDevice.java21
-rw-r--r--app/src/main/java/io/trygvis/android/bt/BtPromise.java (renamed from app/src/main/java/io/trygvis/android/bt/BtActionExecutor.java)136
-rw-r--r--app/src/main/java/io/trygvis/android/bt/DefaultBtService.java2
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java69
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/MainActivity.java10
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/SmDevice.java113
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/SmSensor.java9
-rw-r--r--app/src/main/res/layout/listitem_device.xml9
9 files changed, 314 insertions, 75 deletions
diff --git a/app/src/main/java/io/trygvis/android/bt/BtCallback.java b/app/src/main/java/io/trygvis/android/bt/BtCallback.java
index 29ea9e1..8dab149 100644
--- a/app/src/main/java/io/trygvis/android/bt/BtCallback.java
+++ b/app/src/main/java/io/trygvis/android/bt/BtCallback.java
@@ -4,6 +4,8 @@ import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
+import static io.trygvis.android.bt.BtPromise.*;
+
public class BtCallback {
public final String name;
@@ -11,39 +13,39 @@ public class BtCallback {
this.name = name;
}
- public boolean onConnectionStateChange(BluetoothGatt gatt, int newState) {
+ public PromiseResult onConnectionStateChange(BluetoothGatt gatt, int newState) {
throw new NotOverriddenException();
}
- public boolean onServicesDiscovered(BluetoothGatt gatt) {
+ public PromiseResult onServicesDiscovered(BluetoothGatt gatt) {
throw new NotOverriddenException();
}
- public boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ public PromiseResult onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
throw new NotOverriddenException();
}
- public boolean onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ public PromiseResult onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
throw new NotOverriddenException();
}
- public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ public PromiseResult onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
throw new NotOverriddenException();
}
- public boolean onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
+ public PromiseResult onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
throw new NotOverriddenException();
}
- public boolean onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
+ public PromiseResult onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
throw new NotOverriddenException();
}
- public boolean onReliableWriteCompleted(BluetoothGatt gatt) {
+ public PromiseResult onReliableWriteCompleted(BluetoothGatt gatt) {
throw new NotOverriddenException();
}
- public boolean onReadRemoteRssi(BluetoothGatt gatt, int rssi) {
+ public PromiseResult onReadRemoteRssi(BluetoothGatt gatt, int rssi) {
throw new NotOverriddenException();
}
diff --git a/app/src/main/java/io/trygvis/android/bt/BtDevice.java b/app/src/main/java/io/trygvis/android/bt/BtDevice.java
index edad522..1c7666e 100644
--- a/app/src/main/java/io/trygvis/android/bt/BtDevice.java
+++ b/app/src/main/java/io/trygvis/android/bt/BtDevice.java
@@ -1,6 +1,8 @@
package io.trygvis.android.bt;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
import android.util.Log;
import java.util.Date;
@@ -10,6 +12,7 @@ public class BtDevice<A extends BtDevice.BtDeviceWrapper<A>> {
private final DefaultBtService btService;
private final BluetoothDevice bluetoothDevice;
+ private BluetoothGatt gatt;
private Integer rssi;
private BtScanResult scanResult;
private A tag;
@@ -24,8 +27,8 @@ public class BtDevice<A extends BtDevice.BtDeviceWrapper<A>> {
}
BtDevice(DefaultBtService btService, BluetoothDevice bluetoothDevice,
- BtService.BtDbIntegration<A> btDbIntegration, long id, Integer rssi,
- BtScanResult scanResult, boolean seenBefore, Date firstSeen, Date lastSeen) {
+ BtService.BtDbIntegration<A> btDbIntegration, long id, Integer rssi,
+ BtScanResult scanResult, boolean seenBefore, Date firstSeen, Date lastSeen) {
this.btService = btService;
this.bluetoothDevice = bluetoothDevice;
this.tag = btDbIntegration.createTag(this);
@@ -73,12 +76,20 @@ public class BtDevice<A extends BtDevice.BtDeviceWrapper<A>> {
this.lastSeen = lastSeen;
}
- public boolean connect(BtActionExecutor executor) {
- Log.i(TAG, "connect(), address=" + bluetoothDevice.getAddress() + ", queue=" + executor);
- bluetoothDevice.connectGatt(btService, false, executor.asCallback());
+ public synchronized boolean connect(BtPromise executor) {
+ Log.i(TAG, "connect(), address=" + bluetoothDevice.getAddress());
+ BluetoothGattCallback callback = executor.asCallback();
+ gatt = bluetoothDevice.connectGatt(btService, false, callback);
return true;
}
+ public synchronized void disconnect() {
+ if (gatt != null) {
+ gatt.disconnect();
+ gatt = null;
+ }
+ }
+
public BtScanResult getScanResult() {
return scanResult;
}
diff --git a/app/src/main/java/io/trygvis/android/bt/BtActionExecutor.java b/app/src/main/java/io/trygvis/android/bt/BtPromise.java
index 984c738..55b6315 100644
--- a/app/src/main/java/io/trygvis/android/bt/BtActionExecutor.java
+++ b/app/src/main/java/io/trygvis/android/bt/BtPromise.java
@@ -15,114 +15,134 @@ import java.util.Queue;
import io.trygvis.android.F2;
import io.trygvis.android.Function;
-import static io.trygvis.android.bt.BtActionExecutor.EventType.onCharacteristicChanged;
-import static io.trygvis.android.bt.BtActionExecutor.EventType.onCharacteristicRead;
-import static io.trygvis.android.bt.BtActionExecutor.EventType.onCharacteristicWrite;
-import static io.trygvis.android.bt.BtActionExecutor.EventType.onConnectionStateChange;
-import static io.trygvis.android.bt.BtActionExecutor.EventType.onDescriptorRead;
-import static io.trygvis.android.bt.BtActionExecutor.EventType.onDescriptorWrite;
-import static io.trygvis.android.bt.BtActionExecutor.EventType.onFailure;
-import static io.trygvis.android.bt.BtActionExecutor.EventType.onFinally;
-import static io.trygvis.android.bt.BtActionExecutor.EventType.onReliableWriteCompleted;
-import static io.trygvis.android.bt.BtActionExecutor.EventType.onServicesDiscovered;
-
-public class BtActionExecutor {
- private final static String TAG = BtActionExecutor.class.getSimpleName();
+import static io.trygvis.android.bt.BtPromise.EventType.onCharacteristicChanged;
+import static io.trygvis.android.bt.BtPromise.EventType.onCharacteristicRead;
+import static io.trygvis.android.bt.BtPromise.EventType.onCharacteristicWrite;
+import static io.trygvis.android.bt.BtPromise.EventType.onConnectionStateChange;
+import static io.trygvis.android.bt.BtPromise.EventType.onDescriptorRead;
+import static io.trygvis.android.bt.BtPromise.EventType.onDescriptorWrite;
+import static io.trygvis.android.bt.BtPromise.EventType.onFailure;
+import static io.trygvis.android.bt.BtPromise.EventType.onFinally;
+import static io.trygvis.android.bt.BtPromise.EventType.onReliableWriteCompleted;
+import static io.trygvis.android.bt.BtPromise.EventType.onServicesDiscovered;
+
+public class BtPromise {
+ private final static String TAG = BtPromise.class.getSimpleName();
private final Queue<BtCallback> actionQ = new ArrayDeque<>();
private final Queue<BtCallback> failureQ = new ArrayDeque<>();
private final Queue<BtCallback> finallyQ = new ArrayDeque<>();
- public synchronized BtActionExecutor onConnectionStateChange(F2<BluetoothGatt, Integer, Boolean> callback) {
+ public static final PromiseResult continueDownChain = new Continue();
+ public static final PromiseResult doneWithChain = new Done();
+
+ public abstract static class PromiseResult {
+ }
+
+ private static class Continue extends PromiseResult {
+ }
+
+ private static class Done extends PromiseResult {
+ }
+
+ private static class Detour extends PromiseResult {
+ final BtPromise promise;
+
+ private Detour(BtPromise promise) {
+ this.promise = promise;
+ }
+ }
+
+ public synchronized BtPromise onConnectionStateChange(F2<BluetoothGatt, Integer, PromiseResult> callback) {
actionQ.add(new BtCallback("onConnectionStateChange") {
@Override
- public boolean onConnectionStateChange(BluetoothGatt gatt, int newState) {
+ public PromiseResult onConnectionStateChange(BluetoothGatt gatt, int newState) {
return callback.apply(gatt, newState);
}
});
return this;
}
- public synchronized BtActionExecutor onServicesDiscovered(Function<BluetoothGatt, Boolean> callback) {
+ public synchronized BtPromise onServicesDiscovered(Function<BluetoothGatt, PromiseResult> callback) {
actionQ.add(new BtCallback("onServicesDiscovered") {
@Override
- public boolean onServicesDiscovered(BluetoothGatt gatt) {
+ public PromiseResult onServicesDiscovered(BluetoothGatt gatt) {
return callback.apply(gatt);
}
});
return this;
}
- public synchronized BtActionExecutor onCharacteristicRead(F2<BluetoothGatt, BluetoothGattCharacteristic, Boolean> callback) {
+ public synchronized BtPromise onCharacteristicRead(F2<BluetoothGatt, BluetoothGattCharacteristic, PromiseResult> callback) {
actionQ.add(new BtCallback("onCharacteristicRead") {
@Override
- public boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ public PromiseResult onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
return callback.apply(gatt, characteristic);
}
});
return this;
}
- public synchronized BtActionExecutor onCharacteristicWrite(F2<BluetoothGatt, BluetoothGattCharacteristic, Boolean> callback) {
+ public synchronized BtPromise onCharacteristicWrite(F2<BluetoothGatt, BluetoothGattCharacteristic, PromiseResult> callback) {
actionQ.add(new BtCallback("onCharacteristicWrite") {
@Override
- public boolean onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ public PromiseResult onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
return callback.apply(gatt, characteristic);
}
});
return this;
}
- public synchronized BtActionExecutor onCharacteristicChanged(F2<BluetoothGatt, BluetoothGattCharacteristic, Boolean> callback) {
+ public synchronized BtPromise onCharacteristicChanged(F2<BluetoothGatt, BluetoothGattCharacteristic, PromiseResult> callback) {
actionQ.add(new BtCallback("onCharacteristicChanged") {
@Override
- public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ public PromiseResult onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
return callback.apply(gatt, characteristic);
}
});
return this;
}
- public synchronized BtActionExecutor onDescriptorRead(F2<BluetoothGatt, BluetoothGattDescriptor, Boolean> callback) {
+ public synchronized BtPromise onDescriptorRead(F2<BluetoothGatt, BluetoothGattDescriptor, PromiseResult> callback) {
actionQ.add(new BtCallback("onDescriptorRead") {
@Override
- public boolean onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
+ public PromiseResult onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
return callback.apply(gatt, descriptor);
}
});
return this;
}
- public synchronized BtActionExecutor onDescriptorWrite(F2<BluetoothGatt, BluetoothGattDescriptor, Boolean> callback) {
+ public synchronized BtPromise onDescriptorWrite(F2<BluetoothGatt, BluetoothGattDescriptor, PromiseResult> callback) {
actionQ.add(new BtCallback("onDescriptorWrite") {
@Override
- public boolean onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
+ public PromiseResult onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor) {
return callback.apply(gatt, descriptor);
}
});
return this;
}
- public synchronized BtActionExecutor onReliableWriteCompleted(Function<BluetoothGatt, Boolean> callback) {
+ public synchronized BtPromise onReliableWriteCompleted(Function<BluetoothGatt, PromiseResult> callback) {
actionQ.add(new BtCallback("onReliableWriteCompleted") {
@Override
- public boolean onReliableWriteCompleted(BluetoothGatt gatt) {
+ public PromiseResult onReliableWriteCompleted(BluetoothGatt gatt) {
return callback.apply(gatt);
}
});
return this;
}
- public synchronized BtActionExecutor onReadRemoteRssi(F2<BluetoothGatt, Integer, Boolean> callback) {
+ public synchronized BtPromise onReadRemoteRssi(F2<BluetoothGatt, Integer, PromiseResult> callback) {
actionQ.add(new BtCallback("onReadRemoteRssi") {
@Override
- public boolean onReadRemoteRssi(BluetoothGatt gatt, int rssi) {
+ public PromiseResult onReadRemoteRssi(BluetoothGatt gatt, int rssi) {
return callback.apply(gatt, rssi);
}
});
return this;
}
- public synchronized BtActionExecutor onFailure(Runnable callback) {
+ public synchronized BtPromise onFailure(Runnable callback) {
failureQ.add(new BtCallback("onFailure") {
@Override
public void onFailure() {
@@ -132,7 +152,7 @@ public class BtActionExecutor {
return this;
}
- public synchronized BtActionExecutor onFinally(Runnable callback) {
+ public synchronized BtPromise onFinally(Runnable callback) {
finallyQ.add(new BtCallback("finally") {
@Override
public void onFinally() {
@@ -160,8 +180,12 @@ public class BtActionExecutor {
return s.toString();
}
- public BluetoothGattCallback asCallback() {
- return new MyBluetoothGattCallback();
+ BtBluetoothGattCallback asCallback() {
+ return new BtBluetoothGattCallback(actionQ, failureQ, finallyQ);
+ }
+
+ public PromiseResult toDetour() {
+ return new Detour(this);
}
enum EventType {
@@ -180,7 +204,9 @@ public class BtActionExecutor {
onFinally,
}
- private static Boolean callCallback(EventType key, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor, int newState, BtCallback btCallback) {
+ private static PromiseResult callCallback(EventType key, BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic,
+ BluetoothGattDescriptor descriptor, int newState, BtCallback btCallback) {
switch (key) {
case onConnectionStateChange:
return btCallback.onConnectionStateChange(gatt, newState);
@@ -211,16 +237,25 @@ public class BtActionExecutor {
}
}
- private class MyBluetoothGattCallback extends BluetoothGattCallback {
+ static class BtBluetoothGattCallback extends BluetoothGattCallback {
- private Queue<BtCallback> initialActionQ = new ArrayDeque<>(actionQ);
+ private ArrayDeque<BtCallback> actionQ;
+ private ArrayDeque<BtCallback> failureQ;
+ ArrayDeque<BtCallback> finallyQ;
+// private ArrayDeque<BtCallback> initialActionQ;
private List<String> events = new ArrayList<>();
+ private BtBluetoothGattCallback(Queue<BtCallback> actionQ, Queue<BtCallback> failureQ, Queue<BtCallback> finallyQ) {
+ this.actionQ = new ArrayDeque<>(actionQ);
+ this.failureQ = new ArrayDeque<>(failureQ);
+ this.finallyQ = new ArrayDeque<>(finallyQ);
+ }
+
void onEvent(EventType key, String values, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor, int status, int newState) {
boolean success = status == BluetoothGatt.GATT_SUCCESS;
- events.add(key + ", " + values + ", success=" + success);
+ events.add(key + "(" + values + "), success=" + success);
- Log.i(TAG, "Operation failed: " + key + ", " + values + ", success=" + success);
+ Log.i(TAG, key + "(" + values + "), success=" + success);
if (!success) {
doFailure();
@@ -246,11 +281,24 @@ public class BtActionExecutor {
}
try {
- boolean ok = callCallback(key, gatt, characteristic, descriptor, newState, btCallback);
+ PromiseResult result = callCallback(key, gatt, characteristic, descriptor, newState, btCallback);
- if (!ok) {
- Log.w(TAG, "The callback don't want to continue.");
+ if (result instanceof Done) {
+ Log.i(TAG, "The chain is done.");
doFinally();
+ } else if (result instanceof Detour) {
+ Log.i(TAG, "Adding detour");
+ BtPromise promise = ((Detour) result).promise;
+ if (!promise.failureQ.isEmpty()) {
+ Log.i(TAG, "Ignoring " + promise.failureQ.size() + " items from the failure Q");
+ }
+ if (!promise.finallyQ.isEmpty()) {
+ Log.i(TAG, "Ignoring " + promise.finallyQ.size() + " items from the finally Q");
+ }
+
+ for (BtCallback callback : promise.actionQ) {
+ actionQ.addFirst(callback);
+ }
}
if (actionQ.isEmpty()) {
@@ -266,7 +314,7 @@ public class BtActionExecutor {
StringBuilder msg = new StringBuilder();
msg.append("Expected events: \n");
- for (BtCallback cb : initialActionQ) {
+ for (BtCallback cb : actionQ) {
msg.append(" ").append(cb.name).append("\n");
}
diff --git a/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java b/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java
index 205421b..80a195a 100644
--- a/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java
+++ b/app/src/main/java/io/trygvis/android/bt/DefaultBtService.java
@@ -45,7 +45,7 @@ public class DefaultBtService<A extends BtDevice.BtDeviceWrapper<A>> extends Ser
private BtDbIntegration<A> btDbIntegration;
- private BluetoothManager bluetoothManager;
+ BluetoothManager bluetoothManager;
private BluetoothAdapter bluetoothAdapter;
diff --git a/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java b/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java
index 0f08076..a2723d5 100644
--- a/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java
+++ b/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java
@@ -3,6 +3,7 @@ package io.trygvis.soilmoisture;
import android.app.Service;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.content.ComponentName;
import android.content.ContentValues;
@@ -18,13 +19,17 @@ import java.util.Set;
import java.util.TreeSet;
import io.trygvis.android.LocalBinder;
-import io.trygvis.android.bt.BtActionExecutor;
import io.trygvis.android.bt.BtDevice;
+import io.trygvis.android.bt.BtPromise;
import io.trygvis.android.bt.BtService;
import io.trygvis.android.bt.DefaultBtService;
import io.trygvis.bluetooth.TrygvisIoUuids;
+import static io.trygvis.android.bt.BtPromise.continueDownChain;
+import static io.trygvis.android.bt.BtPromise.doneWithChain;
import static io.trygvis.android.bt.BtService.BtServiceListenerBroadcastReceiver;
+import static io.trygvis.soilmoisture.SmDevice.GetSensorCountRes;
+import static io.trygvis.soilmoisture.SmDevice.SmCmdCode.GET_SENSOR_COUNT;
public class DefaultSoilMoistureService extends Service implements SoilMoistureService {
private final static String TAG = DefaultSoilMoistureService.class.getSimpleName();
@@ -95,37 +100,73 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
if (!smDevice.isProbed()) {
Log.i(TAG, "Probing " + address + ", name=" + btDevice.getName());
- BtActionExecutor executor = new BtActionExecutor().
+ BtPromise executor = new BtPromise().
onConnectionStateChange((gatt, newState) -> {
//noinspection SimplifiableIfStatement
if (newState == BluetoothGatt.STATE_CONNECTED) {
Log.i(TAG, "Connected to " + address + ", getting services");
- return gatt.discoverServices();
- }
+ return gatt.discoverServices() ? continueDownChain : doneWithChain;
+ } else {
+ Log.i(TAG, "Disconnected from " + address + ", trying again");
+
+ return new BtPromise().onConnectionStateChange((gatt2, newState2) -> {
+ if (newState2 == BluetoothGatt.STATE_CONNECTED) {
+ Log.i(TAG, "Connected to " + address + ", getting services");
+ return continueDownChain;
+ }
- Log.i(TAG, "Could not connect to to " + address);
- smDevice.setIsUseful(false);
- return false;
+ Log.i(TAG, "Could still not connect to " + address + ", failing.");
+
+ return doneWithChain;
+ }).toDetour();
+ }
}).
onServicesDiscovered(gatt -> {
Log.i(TAG, "Services discovered");
BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE);
- boolean useful = false;
- if (service != null) {
- BluetoothGattCharacteristic characteristic = service.getCharacteristic(TrygvisIoUuids.Characteristics.SOIL_MOISTURE);
+ if (service == null) {
+ return doneWithChain;
+ }
- useful = characteristic != null;
+ BluetoothGattCharacteristic soilMoisture = service.getCharacteristic(TrygvisIoUuids.Characteristics.SOIL_MOISTURE);
- smDevice.setIsUseful(useful);
+ if (soilMoisture == null) {
+ return doneWithChain;
}
- gatt.disconnect();
+ BluetoothGattDescriptor ccg = soilMoisture.getDescriptor(TrygvisIoUuids.CLIENT_CHARACTERISTIC_CONFIG);
+ ccg.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
+ gatt.setCharacteristicNotification(soilMoisture, true);
- return false;
+ return gatt.writeDescriptor(ccg) ? continueDownChain : doneWithChain;
+ }).
+ onDescriptorWrite((gatt, descriptor) -> {
+ Log.i(TAG, "Notifications enabled, getting sensor count");
+ BluetoothGattCharacteristic c = descriptor.getCharacteristic();
+ c.setValue(SmDevice.createGetSensorCountReq());
+ return gatt.writeCharacteristic(c) ? continueDownChain : doneWithChain;
+ }).
+ onCharacteristicWrite((gatt, characteristic) -> continueDownChain).
+ onCharacteristicChanged((gatt, characteristic) -> {
+ GetSensorCountRes getSensorCountRes = SmDevice.parseResponse(characteristic.getValue(),
+ GET_SENSOR_COUNT, GetSensorCountRes.class);
+
+ Log.i(TAG, "The device has " + getSensorCountRes.count + " sensors.");
+
+ smDevice.setIsUseful(true);
+ smDevice.setSensorCount(getSensorCountRes.count);
+
+ return doneWithChain;
}).
onFinally(() -> {
+ btDevice.disconnect();
+
+ if (smDevice.getIsUseful() == null) {
+ smDevice.setIsUseful(false);
+ }
+
btService.runTx(db -> {
if (!btDevice.isSeenBefore() && smDevice.isUseful()) {
for (SmSensor soilMonitor : smDevice.getSensors()) {
diff --git a/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java b/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java
index 2e6df76..7aa3534 100644
--- a/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java
+++ b/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java
@@ -222,6 +222,7 @@ public class MainActivity extends ListActivity {
final TextView deviceName;
final TextView deviceAddress;
final TextView rssi;
+ final TextView info;
final ProgressBar spinner;
final Button connect;
@@ -229,6 +230,7 @@ public class MainActivity extends ListActivity {
this.deviceName = (TextView) view.findViewById(R.id.device_name);
this.deviceAddress = (TextView) view.findViewById(R.id.device_address);
this.rssi = (TextView) view.findViewById(R.id.device_rssi);
+ this.info = (TextView) view.findViewById(R.id.device_info);
this.spinner = (ProgressBar) view.findViewById(R.id.device_spinner);
this.connect = (Button) view.findViewById(R.id.button_connect);
}
@@ -241,7 +243,7 @@ public class MainActivity extends ListActivity {
private LayoutInflater inflater = MainActivity.this.getLayoutInflater();
private boolean groupByDevice = true;
- private boolean showAll = false;
+ private boolean showAll = true;
public void sort() {
Log.i(TAG, "sort(), groupByDevice=" + groupByDevice + ", showAll=" + showAll);
@@ -400,6 +402,12 @@ public class MainActivity extends ListActivity {
item.rssi.setText(getText(R.string.rssi) + ": " +
(smDevice.getBtDevice().getRssi() != 0 ? valueOf(smDevice.getBtDevice().getRssi()) : getText(R.string.unknown)));
+ if (smDevice.isUseful()) {
+ item.info.setText("number of sensors: " + smDevice.getSensors().size());
+ } else {
+ item.info.setText("");
+ }
+
boolean useful = smDevice.isUseful();
item.spinner.setVisibility(useful ? View.GONE : View.VISIBLE);
item.connect.setVisibility(useful ? View.VISIBLE : View.GONE);
diff --git a/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java b/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java
index e24416c..b51b3fa 100644
--- a/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java
+++ b/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java
@@ -2,11 +2,19 @@ package io.trygvis.soilmoisture;
import android.util.Log;
+import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import io.trygvis.android.bt.BtDevice;
+import static io.trygvis.soilmoisture.SmDevice.SmCmdCode.GET_SENSOR_COUNT;
+import static io.trygvis.soilmoisture.SmDevice.SmCmdCode.GET_SENSOR_NAME;
+import static io.trygvis.soilmoisture.SmDevice.SmCmdCode.GET_VALUE;
+import static io.trygvis.soilmoisture.SmDevice.SmCmdCode.GET_WARNING_VALUE;
+import static io.trygvis.soilmoisture.SmDevice.SmCmdCode.SET_SENSOR_NAME;
+import static io.trygvis.soilmoisture.SmDevice.SmCmdCode.SET_WARNING_VALUE;
+
class SmDevice implements BtDevice.BtDeviceWrapper<SmDevice> {
private final static String TAG = SmDevice.class.getSimpleName();
@@ -57,4 +65,109 @@ class SmDevice implements BtDevice.BtDeviceWrapper<SmDevice> {
public List<SmSensor> getSensors() {
return sensors;
}
+
+ // -----------------------------------------------------------------------
+ //
+ // -----------------------------------------------------------------------
+
+ @SuppressWarnings("unchecked")
+ public static <T> T parseResponse(byte[] bytes, SmCmdCode code, Class<T> klass) {
+ byte c = bytes[0];
+
+ if (c != code.code) {
+ throw new RuntimeException("Expected response of type " + code + ", got " + c);
+ }
+
+ Object value = null;
+ if (c == GET_SENSOR_COUNT.code) {
+ return (T) new GetSensorCountRes(bytes[1]);
+ } else if (c == GET_VALUE.code) {
+ } else if (c == SET_WARNING_VALUE.code) {
+ } else if (c == GET_WARNING_VALUE.code) {
+ } else if (c == SET_SENSOR_NAME.code) {
+ } else if (c == GET_SENSOR_NAME.code) {
+ }
+
+ throw new RuntimeException("Unknown code: " + c);
+ }
+
+ public void setSensorCount(int count) {
+ sensors = new ArrayList<>();
+ for (int index = 0; index < count; index++) {
+ sensors.add(new SmSensor(this, index));
+ }
+ }
+
+ public static class GetSensorCountRes {
+ public final int count;
+
+ public GetSensorCountRes(int count) {
+ this.count = count;
+ }
+ }
+
+ public static byte[] createGetSensorCountReq() {
+ return new byte[]{
+ GET_SENSOR_COUNT.code
+ };
+ }
+
+ public static byte[] createGetValueReq(byte sensor) {
+ return new byte[]{
+ GET_VALUE.code,
+ sensor
+ };
+ }
+
+ public static byte[] createSetWarningValueReq(byte sensor, short warningValue) {
+ return new byte[]{
+ SET_WARNING_VALUE.code,
+ sensor,
+ (byte) (warningValue & 0xff),
+ (byte) ((warningValue >> 8) & 0xff)
+ };
+ }
+
+ public static byte[] createGetWarningValueReq(byte sensor) {
+ return new byte[]{
+ GET_WARNING_VALUE.code,
+ sensor
+ };
+ }
+
+ public static byte[] createSetSensorNameReq(byte sensor, String name) {
+ byte[] tmp = name.getBytes(Charset.defaultCharset());
+
+ byte[] bytes = new byte[3 + tmp.length];
+ bytes[0] = GET_WARNING_VALUE.code;
+ bytes[1] = sensor;
+ bytes[2] = (byte) tmp.length;
+ System.arraycopy(tmp, 0, bytes, 1, tmp.length);
+ return bytes;
+ }
+
+ public static byte[] createGetSensorNameReq(byte sensor) {
+ return new byte[]{
+ GET_SENSOR_NAME.code,
+ sensor
+ };
+ }
+
+ public static final int SENSOR_NAME_LEN = 10;
+
+ public enum SmCmdCode {
+ GET_SENSOR_COUNT(1),
+ GET_VALUE(2),
+ SET_WARNING_VALUE(3),
+ GET_WARNING_VALUE(4),
+ SET_SENSOR_NAME(5),
+ GET_SENSOR_NAME(6),
+ FAIL(255);
+
+ public final byte code;
+
+ SmCmdCode(int value) {
+ this.code = (byte) value;
+ }
+ }
}
diff --git a/app/src/main/java/io/trygvis/soilmoisture/SmSensor.java b/app/src/main/java/io/trygvis/soilmoisture/SmSensor.java
index f27c364..ec4b423 100644
--- a/app/src/main/java/io/trygvis/soilmoisture/SmSensor.java
+++ b/app/src/main/java/io/trygvis/soilmoisture/SmSensor.java
@@ -5,18 +5,25 @@ import java.util.Date;
class SmSensor {
private final SmDevice device;
+ private final int index;
+
private Date timestamp;
private int lastValue;
- SmSensor(SmDevice device) {
+ SmSensor(SmDevice device, int index) {
this.device = device;
+ this.index = index;
}
public SmDevice getDevice() {
return device;
}
+ public int getIndex() {
+ return index;
+ }
+
public int getLastValue() {
return lastValue;
}
diff --git a/app/src/main/res/layout/listitem_device.xml b/app/src/main/res/layout/listitem_device.xml
index 0904d94..44021d9 100644
--- a/app/src/main/res/layout/listitem_device.xml
+++ b/app/src/main/res/layout/listitem_device.xml
@@ -31,6 +31,15 @@
android:layout_alignParentStart="true"
android:layout_toStartOf="@+id/spacer"/>
+ <TextView
+ android:id="@+id/device_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="12sp"
+ android:layout_below="@+id/device_rssi"
+ android:layout_alignParentStart="true"
+ android:layout_toStartOf="@+id/spacer"/>
+
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"