aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java')
-rw-r--r--app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java231
1 files changed, 178 insertions, 53 deletions
diff --git a/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java b/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java
index a2723d5..3ff6e66 100644
--- a/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java
+++ b/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java
@@ -9,12 +9,17 @@ import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
import android.os.IBinder;
import android.util.Log;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
@@ -25,15 +30,24 @@ 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.BtPromise.PromiseResult.continueDirectly;
+import static io.trygvis.android.bt.BtPromise.PromiseResult.detour;
+import static io.trygvis.android.bt.BtPromise.PromiseResult.stop;
+import static io.trygvis.android.bt.BtPromise.PromiseResult.waitForNextEvent;
import static io.trygvis.android.bt.BtService.BtServiceListenerBroadcastReceiver;
+import static io.trygvis.bluetooth.TrygvisIoUuids.CLIENT_CHARACTERISTIC_CONFIG;
import static io.trygvis.soilmoisture.SmDevice.GetSensorCountRes;
+import static io.trygvis.soilmoisture.SmDevice.GetValueRes;
import static io.trygvis.soilmoisture.SmDevice.SmCmdCode.GET_SENSOR_COUNT;
+import static io.trygvis.soilmoisture.SmDevice.SmCmdCode.GET_VALUE;
+import static io.trygvis.soilmoisture.SmDevice.createGetSensorCountReq;
+import static io.trygvis.soilmoisture.SmDevice.createGetValueReq;
+import static io.trygvis.soilmoisture.SmDevice.parseResponse;
+import static java.lang.String.valueOf;
+import static java.lang.System.currentTimeMillis;
public class DefaultSoilMoistureService extends Service implements SoilMoistureService {
private final static String TAG = DefaultSoilMoistureService.class.getSimpleName();
- private final static int DEFAULT_WARNING_LEVEL = 750;
private final IBinder binder = new LocalBinder<>(this);
@@ -58,7 +72,7 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
btService = ((LocalBinder<BtService<SmDevice>>) service).getService();
- boolean ok = btService.initialize(DefaultSoilMoistureService.this::onNewDevice);
+ boolean ok = btService.initialize(DefaultSoilMoistureService.this::createTag);
sendBroadcast(createReady(ok));
}
@@ -98,28 +112,16 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
BtDevice<SmDevice> btDevice = btService.getDevice(address);
SmDevice smDevice = btDevice.getTag();
+ sendBroadcast(createNewDevice(address));
+
if (!smDevice.isProbed()) {
Log.i(TAG, "Probing " + address + ", name=" + btDevice.getName());
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() ? 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 still not connect to " + address + ", failing.");
-
- return doneWithChain;
- }).toDetour();
- }
+ ignoreFailureForNext().
+ onConnectionStateChange(DefaultSoilMoistureService::defaultConnectCallback).
+ onDirect(v -> {
+ BluetoothGatt gatt = (BluetoothGatt) v;
+ return gatt.discoverServices() ? waitForNextEvent() : stop();
}).
onServicesDiscovered(gatt -> {
Log.i(TAG, "Services discovered");
@@ -127,38 +129,37 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE);
if (service == null) {
- return doneWithChain;
+ return stop();
}
BluetoothGattCharacteristic soilMoisture = service.getCharacteristic(TrygvisIoUuids.Characteristics.SOIL_MOISTURE);
if (soilMoisture == null) {
- return doneWithChain;
+ return stop();
}
BluetoothGattDescriptor ccg = soilMoisture.getDescriptor(TrygvisIoUuids.CLIENT_CHARACTERISTIC_CONFIG);
ccg.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.setCharacteristicNotification(soilMoisture, true);
- return gatt.writeDescriptor(ccg) ? continueDownChain : doneWithChain;
+ return gatt.writeDescriptor(ccg) ? waitForNextEvent() : stop();
}).
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;
+ c.setValue(createGetSensorCountReq());
+ return gatt.writeCharacteristic(c) ? waitForNextEvent() : stop();
}).
- onCharacteristicWrite((gatt, characteristic) -> continueDownChain).
+ onCharacteristicWrite((gatt, characteristic) -> waitForNextEvent()).
onCharacteristicChanged((gatt, characteristic) -> {
- GetSensorCountRes getSensorCountRes = SmDevice.parseResponse(characteristic.getValue(),
+ GetSensorCountRes getSensorCountRes = parseResponse(characteristic.getValue(),
GET_SENSOR_COUNT, GetSensorCountRes.class);
Log.i(TAG, "The device has " + getSensorCountRes.count + " sensors.");
- smDevice.setIsUseful(true);
- smDevice.setSensorCount(getSensorCountRes.count);
+ markDeviceAsUseful(smDevice, getSensorCountRes.count);
- return doneWithChain;
+ return stop();
}).
onFinally(() -> {
btDevice.disconnect();
@@ -167,28 +168,69 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
smDevice.setIsUseful(false);
}
- btService.runTx(db -> {
- if (!btDevice.isSeenBefore() && smDevice.isUseful()) {
- for (SmSensor soilMonitor : smDevice.getSensors()) {
- ContentValues values = new ContentValues();
- values.put("bt_device", btDevice.getId());
- values.put("warning_level", DEFAULT_WARNING_LEVEL);
- db.insert("soil_monitor", null, values);
- }
- }
-
- return null;
- });
- sendBroadcast(createNewDevice(address));
});
btDevice.connect(executor);
- } else {
- sendBroadcast(createNewDevice(address));
}
}
};
+ private void markDeviceAsUseful(SmDevice device, int sensorCount) {
+ btService.runTx(db -> {
+ device.setIsUseful(true);
+
+ // Find all already registered sensors and register the rest
+ Map<Integer, Long> indexes = new HashMap<>();
+ Cursor cursor = db.query(Tables.T_SM_SENSOR, new String[]{Tables.C_ID, Tables.C_INDEX},
+ Tables.C_SM_DEVICE + "=?", new String[]{valueOf(device.id)}, null, null, null);
+ while (cursor.moveToNext()) {
+ indexes.put(cursor.getInt(1), cursor.getLong(0));
+ }
+
+ ContentValues values = new ContentValues();
+ values.put(Tables.C_USEFUL, true);
+ db.update(Tables.T_SM_DEVICE, values, "id=?", new String[]{valueOf(device.id)});
+
+ for (int i = 0; i < sensorCount; i++) {
+ Long id = indexes.get(i);
+
+ if (id == null) {
+ values = new ContentValues();
+ values.put(Tables.C_SM_DEVICE, device.id);
+ values.put(Tables.C_INDEX, i);
+ id = db.insert(Tables.T_SM_SENSOR, null, values);
+ }
+
+ device.addSensor(new SmSensor(device, id, (byte) i));
+ }
+
+ return null;
+ });
+ }
+
+ public static BtPromise.PromiseResult defaultConnectCallback(BluetoothGatt gatt, int status, Integer newState) {
+ Log.i(TAG, "defaultConnectCallback: status=" + status + ", newState=" + newState);
+ String address = gatt.getDevice().getAddress();
+ //noinspection SimplifiableIfStatement
+ if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothGatt.STATE_CONNECTED) {
+ Log.i(TAG, "Connected to " + address);
+ return continueDirectly(gatt);
+ } else {
+ Log.i(TAG, "Disconnected from " + address + ", trying again");
+
+ return detour(new BtPromise().onConnectionStateChange((gatt2, status2, newState2) -> {
+ if (newState2 == BluetoothGatt.STATE_CONNECTED) {
+ Log.i(TAG, "Connected to " + address);
+ return continueDirectly(gatt);
+ }
+
+ Log.i(TAG, "Could still not connect to " + address + ", failing.");
+
+ return stop();
+ }));
+ }
+ }
+
// -----------------------------------------------------------------------
// SmDevicesManager Implementation
// -----------------------------------------------------------------------
@@ -223,13 +265,82 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
}
// -----------------------------------------------------------------------
- // Event creation and dispatching
+ //
// -----------------------------------------------------------------------
- private SmDevice onNewDevice(BtDevice<SmDevice> btDevice) {
- return new SmDevice(btDevice);
+ private SmDevice createTag(SQLiteDatabase db, BtDevice<SmDevice> btDevice) {
+ Cursor cursor = db.query(Tables.T_SM_DEVICE, new String[]{Tables.C_ID}, Tables.C_BT_DEVICE + "=?",
+ new String[]{valueOf(btDevice.getId())}, null, null, null);
+
+ long id;
+ if (cursor.moveToNext()) {
+ id = cursor.getLong(0);
+ } else {
+ ContentValues values = new ContentValues();
+ values.put(Tables.C_BT_DEVICE, btDevice.getId());
+ id = db.insert(Tables.T_SM_DEVICE, null, values);
+ }
+
+ return new SmDevice(this, btDevice, id);
+ }
+
+ void handleNewSensorValueReady(SmSensor sensor, int value) {
+ long timestamp = currentTimeMillis();
+ btService.runTx(db -> {
+ ContentValues values = new ContentValues();
+ values.put(Tables.C_SM_SENSOR, sensor.getId());
+ values.put(Tables.C_TIMESTAMP, timestamp);
+ values.put(Tables.C_VALUE, value);
+ return db.insert(Tables.T_SOIL_SAMPLE, null, values);
+ });
+
+ sensor.updateLastValue(new Date(timestamp), value);
+ sendBroadcast(createNewSample(sensor));
+ }
+
+ void readCurrentValue(SmSensor sensor) {
+ BtPromise promise = new BtPromise().
+ onConnectionStateChange(DefaultSoilMoistureService::defaultConnectCallback).
+ onDirect(v -> {
+ BluetoothGatt gatt = (BluetoothGatt) v;
+ return gatt.discoverServices() ? waitForNextEvent() : stop();
+ }).
+ onServicesDiscovered(gatt -> {
+ BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE);
+ BluetoothGattCharacteristic soilMoisture = service.getCharacteristic(TrygvisIoUuids.Characteristics.SOIL_MOISTURE);
+
+ BluetoothGattDescriptor ccg = soilMoisture.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG);
+ ccg.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
+ gatt.setCharacteristicNotification(soilMoisture, true);
+
+ return gatt.writeDescriptor(ccg) ? waitForNextEvent() : stop();
+ }).
+ onDescriptorWrite((gatt, descriptor) -> {
+ BluetoothGattCharacteristic c = descriptor.getCharacteristic();
+ c.setValue(createGetValueReq((byte) sensor.getIndex()));
+ return gatt.writeCharacteristic(c) ? waitForNextEvent() : stop();
+ }).
+ onCharacteristicWrite((gatt, characteristic) -> waitForNextEvent()).
+ onCharacteristicChanged((gatt, characteristic) -> {
+ GetValueRes getSensorCountRes = parseResponse(characteristic.getValue(),
+ GET_VALUE, GetValueRes.class);
+
+ handleNewSensorValueReady(sensor, getSensorCountRes.value);
+
+ gatt.disconnect();
+
+ return stop();
+ }).
+ onFinally(() -> {
+ });
+
+ sensor.getDevice().getBtDevice().connect(promise);
}
+ // -----------------------------------------------------------------------
+ // Event creation and dispatching
+ // -----------------------------------------------------------------------
+
private Intent createReady(boolean success) {
return new Intent(SoilMoistureListener.INTENT_NAME).
putExtra("event", "ready").
@@ -252,15 +363,24 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
putExtra("address", address);
}
+ private Intent createNewSample(SmSensor sensor) {
+ return new Intent(SoilMoistureListener.INTENT_NAME).
+ putExtra("event", "newSample").
+ putExtra("address", sensor.getDevice().getBtDevice().getAddress()).
+ putExtra("index", sensor.getIndex());
+ }
+
public static void dispatchEvent(Intent intent, SoilMoistureListener listener) {
String event = intent.getStringExtra("event");
Log.i(TAG, "Dispatching event " + intent.getAction() + "/" + event);
switch (event) {
case "ready":
- listener.onReady(intent.getBooleanExtra("success", false));
+ listener.onReady(
+ intent.getBooleanExtra("success", false));
break;
case "newDevice":
- listener.onNewDevice(intent.getStringExtra("address"));
+ listener.onNewDevice(
+ intent.getStringExtra("address"));
break;
case "scanStarted":
listener.onScanStarted();
@@ -268,6 +388,11 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS
case "scanStopped":
listener.onScanStopped();
break;
+ case "newSample":
+ listener.onNewSample(
+ intent.getStringExtra("address"),
+ intent.getIntExtra("index", -1));
+ break;
default:
break;
}