From 138b49247a72890df66af9a01e1179dab72a4b71 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Tue, 20 Jan 2015 00:19:22 +0100 Subject: o Adding a way to append promises to promises. o Reading each sensors name when probing a device. o Enabling probing on long presses on already probed devices. Is probably useful to update the local information when other handsets has updated the device's values. --- .../main/java/io/trygvis/android/bt/BtPromise.java | 6 ++ .../soilmoisture/DefaultSoilMoistureService.java | 90 +++++++++++++++++----- .../java/io/trygvis/soilmoisture/MainActivity.java | 21 +++-- .../java/io/trygvis/soilmoisture/SmDevice.java | 17 +++- .../java/io/trygvis/soilmoisture/SmSensor.java | 5 ++ .../trygvis/soilmoisture/SoilMoistureService.java | 3 + 6 files changed, 110 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/io/trygvis/android/bt/BtPromise.java b/app/src/main/java/io/trygvis/android/bt/BtPromise.java index edb2894..50aad13 100644 --- a/app/src/main/java/io/trygvis/android/bt/BtPromise.java +++ b/app/src/main/java/io/trygvis/android/bt/BtPromise.java @@ -218,6 +218,12 @@ public class BtPromise { return this; } + public BtPromise andThen(BtPromise btPromise) { + actionQ.addAll(btPromise.actionQ); + finallyQ.addAll(btPromise.finallyQ); + return this; + } + public String toString() { StringBuilder s = new StringBuilder("Queue: "); diff --git a/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java b/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java index 86a1917..85a6afb 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java +++ b/app/src/main/java/io/trygvis/soilmoisture/DefaultSoilMoistureService.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import io.trygvis.android.Function; import io.trygvis.android.LocalBinder; import io.trygvis.android.bt.BtDevice; import io.trygvis.android.bt.BtPromise; @@ -30,15 +31,20 @@ import io.trygvis.android.bt.BtService; import io.trygvis.android.bt.DefaultBtService; import io.trygvis.bluetooth.TrygvisIoUuids; +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.GetSensorNameRes; 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_SENSOR_NAME; import static io.trygvis.soilmoisture.SmDevice.SmCmdCode.GET_VALUE; import static io.trygvis.soilmoisture.SmDevice.createGetSensorCountReq; +import static io.trygvis.soilmoisture.SmDevice.createGetSensorNameReq; import static io.trygvis.soilmoisture.SmDevice.createGetValueReq; import static io.trygvis.soilmoisture.SmDevice.parseResponse; import static java.lang.String.valueOf; @@ -127,12 +133,47 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS } }; + private BtPromise readAttribute(String value, byte[] req, Function handler) { + return new BtPromise(). + onDirect(gatt -> { + Log.i(TAG, "Getting attribute: " + value); + + BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE); + BluetoothGattCharacteristic soilMoisture = service.getCharacteristic(TrygvisIoUuids.Characteristics.SOIL_MOISTURE); + + soilMoisture.setValue(req); + return gatt.writeCharacteristic(soilMoisture) ? waitForNextEvent() : stop(); + }). + onCharacteristicWrite((gatt, characteristic) -> waitForNextEvent()). + onCharacteristicChanged((gatt, characteristic) -> { + byte[] bytes = characteristic.getValue(); + + return handler.apply(bytes); + }); + } + + private BtPromise readSensorName(SmDevice device, int index) { + byte[] req = createGetSensorNameReq((byte) index); + + return readAttribute("sensor name, index#" + index, req, bytes -> { + GetSensorNameRes res = parseResponse(bytes, GET_SENSOR_NAME, GetSensorNameRes.class); + String name = res.name; + + device.getSensorByIndex(index).ifPresent(sensor -> { + sensor.setName(name); + sendBroadcast(createDevicePropertyUpdated(device)); + }); + + return index == device.getSensors().size() - 1 ? stop() : detour(readSensorName(device, index + 1)); + }); + } + public void probe(String address) { BtDevice btDevice = btService.getDevice(address); SmDevice smDevice = btDevice.getTag(); Log.i(TAG, "Probing " + address + ", name=" + btDevice.getName()); - BtPromise executor = new BtPromise(). + BtPromise promise = new BtPromise(). onDirect(gatt -> { BluetoothGattService service = gatt.getService(TrygvisIoUuids.Services.SOIL_MOISTURE_SERVICE); @@ -153,21 +194,8 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS return gatt.writeDescriptor(ccg) ? waitForNextEvent() : stop(); }). onDescriptorWrite((gatt, descriptor) -> { - Log.i(TAG, "Notifications enabled, getting sensor count"); - BluetoothGattCharacteristic c = descriptor.getCharacteristic(); - c.setValue(createGetSensorCountReq()); - return gatt.writeCharacteristic(c) ? waitForNextEvent() : stop(); - }). - onCharacteristicWrite((gatt, characteristic) -> waitForNextEvent()). - onCharacteristicChanged((gatt, characteristic) -> { - GetSensorCountRes getSensorCountRes = parseResponse(characteristic.getValue(), - GET_SENSOR_COUNT, GetSensorCountRes.class); - - Log.i(TAG, "The device has " + getSensorCountRes.count + " sensors."); - - markDeviceAsUseful(smDevice, getSensorCountRes.count); - - return stop(); + Log.i(TAG, "Notifications enabled"); + return continueDirectly(); }). onFinally(success -> { if (smDevice.getIsUseful() == null) { @@ -175,7 +203,20 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS } }); - btDevice.withConnection(executor); + BtPromise btPromise = readAttribute("sensor count", createGetSensorCountReq(), bytes -> { + GetSensorCountRes getSensorCountRes = parseResponse(bytes, GET_SENSOR_COUNT, GetSensorCountRes.class); + + int count = getSensorCountRes.count; + + Log.i(TAG, "The device has " + count + " sensors."); + + markDeviceAsUseful(smDevice, count); + + return detour(readSensorName(smDevice, 0)); + }); + promise.andThen(btPromise); + + btDevice.withConnection(promise); } private void markDeviceAsNotUseful(SmDevice device) { @@ -214,9 +255,9 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS 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)); + device.addSensor(new SmSensor(device, id, (byte) i)); + } } return null; @@ -393,6 +434,12 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS putExtra("index", sensor.getIndex()); } + private Intent createDevicePropertyUpdated(SmDevice device) { + return new Intent(SoilMoistureListener.INTENT_NAME). + putExtra("event", "devicePropertyUpdated"). + putExtra("address", device.getBtDevice().getAddress()); + } + public static void dispatchEvent(Intent intent, SoilMoistureListener listener) { String event = intent.getStringExtra("event"); Log.i(TAG, "Dispatching event " + intent.getAction() + "/" + event); @@ -421,7 +468,12 @@ public class DefaultSoilMoistureService extends Service implements SoilMoistureS intent.getStringExtra("address"), intent.getIntExtra("index", -1)); break; + case "devicePropertyUpdated": + listener.onDevicePropertyUpdated( + intent.getStringExtra("address")); + break; default: + Log.w(TAG, "Unknown event: " + event); break; } } diff --git a/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java b/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java index 79b09c4..e838f01 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java +++ b/app/src/main/java/io/trygvis/soilmoisture/MainActivity.java @@ -216,7 +216,7 @@ public class MainActivity extends ListActivity { // // ----------------------------------------------------------------------- - public static class BtDeviceDialogFragment extends DialogFragment { + public static class ProbeDeviceDialogFragment extends DialogFragment { private MainActivity mainActivity; @@ -243,17 +243,19 @@ public class MainActivity extends ListActivity { } private boolean onBtDeviceLongClick(BtDevice device) { - Log.i(TAG, "onBtDeviceClick, device=" + device.getId()); - - BtDeviceDialogFragment dialog = new BtDeviceDialogFragment(); + ProbeDeviceDialogFragment dialog = new ProbeDeviceDialogFragment(); Bundle arguments = new Bundle(); arguments.putString("address", device.getAddress()); dialog.setArguments(arguments); - dialog.show(getFragmentManager(), "BtDeviceDialogFragment"); + dialog.show(getFragmentManager(), "ProbeDeviceDialogFragment"); return true; } + private boolean onSmDeviceLongClick(SmDevice device) { + return onBtDeviceLongClick(device.getBtDevice()); + } + private void onSmDeviceClick(SmDevice device) { Log.i(TAG, "onSmDeviceClick, device=" + device.getBtDevice().getId()); } @@ -509,13 +511,8 @@ public class MainActivity extends ListActivity { if (view == null) { view = inflater.inflate(R.layout.fragment_device, null); view.setTag(new DeviceItem(view)); - view.setOnClickListener(v -> { - Log.i(TAG, "onClick: SmDevice, " + -// "position=" + position + ", " + - "device=" + smDevice /*+ ", " + - "tag=" + v.getTag()*/); - onSmDeviceClick(smDevice); - }); + view.setOnClickListener(v -> onSmDeviceClick(smDevice)); + view.setOnLongClickListener(v -> onSmDeviceLongClick(smDevice)); } DeviceItem item = (DeviceItem) view.getTag(); diff --git a/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java b/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java index d56f33e..bf2b5f3 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java +++ b/app/src/main/java/io/trygvis/soilmoisture/SmDevice.java @@ -3,6 +3,7 @@ package io.trygvis.soilmoisture; import android.util.Log; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -11,6 +12,7 @@ import io.trygvis.android.bt.BtDevice; import static io.trygvis.android.Optional.empty; import static io.trygvis.android.Optional.of; +import static io.trygvis.android.bt.BtUtils.toHexString; 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; @@ -114,7 +116,7 @@ class SmDevice { // Message parsing and handling. // ----------------------------------------------------------------------- - @SuppressWarnings({"unchecked", "StatementWithEmptyBody"}) + @SuppressWarnings({"unchecked", "StatementWithEmptyBody", "UnusedParameters"}) public static T parseResponse(byte[] bytes, SmCmdCode code, Class klass) { byte c = bytes[0]; @@ -130,6 +132,7 @@ class SmDevice { } else if (c == GET_WARNING_VALUE.code) { } else if (c == SET_SENSOR_NAME.code) { } else if (c == GET_SENSOR_NAME.code) { + return (T) new GetSensorNameRes(bytes); } throw new RuntimeException("Unknown code: " + c); @@ -151,6 +154,18 @@ class SmDevice { } } + public static class GetSensorNameRes { + public final String name; + + public GetSensorNameRes(byte[] bytes) { + int count = bytes[1]; + + Log.i(TAG, "count=" + count + ", bytes=" + toHexString(bytes)); + + name = new String(bytes, 2, Math.min(bytes.length - 1, count), StandardCharsets.UTF_8); + } + } + public static byte[] createGetSensorCountReq() { return new byte[]{ GET_SENSOR_COUNT.code diff --git a/app/src/main/java/io/trygvis/soilmoisture/SmSensor.java b/app/src/main/java/io/trygvis/soilmoisture/SmSensor.java index 0f49027..3c6cac3 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/SmSensor.java +++ b/app/src/main/java/io/trygvis/soilmoisture/SmSensor.java @@ -38,6 +38,10 @@ class SmSensor { return name; } + public void setName(String name) { + this.name = name; + } + public Integer getLastValue() { return lastValue; } @@ -53,6 +57,7 @@ class SmSensor { public String toString() { return "SmSensor[id=" + id + ", " + "device=" + device.getBtDevice().getAddress() + ", " + + "name=" + (name != null ? name : "unknown") + ", " + "index=" + getIndex() + "]"; } diff --git a/app/src/main/java/io/trygvis/soilmoisture/SoilMoistureService.java b/app/src/main/java/io/trygvis/soilmoisture/SoilMoistureService.java index b7f2060..352cba3 100644 --- a/app/src/main/java/io/trygvis/soilmoisture/SoilMoistureService.java +++ b/app/src/main/java/io/trygvis/soilmoisture/SoilMoistureService.java @@ -52,5 +52,8 @@ public interface SoilMoistureService { public void onNewSample(String address, int sensor) { } + + public void onDevicePropertyUpdated(String address) { + } } } -- cgit v1.2.3