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.Comparator; import java.util.List; import io.trygvis.android.Optional; 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; 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 { private final static String TAG = SmDevice.class.getSimpleName(); final DefaultSoilMoistureService smService; private final BtDevice btDevice; final long id; private String name; private Boolean isUseful; private List sensors = new ArrayList<>(); public static Comparator addressComparator = new Comparator() { @Override public int compare(SmDevice lhs, SmDevice rhs) { return lhs.btDevice.getAddress().compareTo(rhs.btDevice.getAddress()); } }; public SmDevice(DefaultSoilMoistureService smService, BtDevice btDevice, long id) { this.smService = smService; this.btDevice = btDevice; this.id = id; Log.i(TAG, "new device"); name = btDevice.getName(); if (name != null && name.trim().length() == 0) { name = null; } } public BtDevice getBtDevice() { return btDevice; } public boolean isUseful() { return isUseful != null && isUseful; } public Boolean getIsUseful() { return isUseful; } public boolean isProbed() { return isUseful != null; } public void setIsUseful(Boolean isUseful) { Log.i(TAG, "useful=" + isUseful); this.isUseful = isUseful; } public String getName() { return name; } public List getSensors() { if (!isUseful()) { throw new IllegalStateException("Not a useful device"); } return sensors; } public Optional getSensorByNumber(int index) { if (!isUseful()) { throw new IllegalStateException("Not a useful device"); } for (SmSensor sensor : sensors) { if (sensor.getIndex() == index) { return of(sensor); } } return empty(); } void addSensor(SmSensor sensor) { if (getSensorByNumber(sensor.index).isPresent()) { throw new IllegalStateException("This device already contains a sensor with index=" + sensor.index); } sensors.add(sensor); } public long getId() { return id; } public String toString() { return "SmDevice[id=" + id + ", " + "address=" + btDevice.getAddress() + ", " + "#sensors=" + sensors.size() + "]"; } // ----------------------------------------------------------------------- // Message parsing and handling. // ----------------------------------------------------------------------- @SuppressWarnings({"unchecked", "StatementWithEmptyBody", "UnusedParameters"}) public static T parseResponse(byte[] bytes, SmCmdCode code, Class klass) { byte c = bytes[0]; if (c != code.code) { throw new RuntimeException("Expected response of type " + code + ", got " + c); } if (c == GET_SENSOR_COUNT.code) { return (T) new GetSensorCountRes(bytes[1]); } else if (c == GET_VALUE.code) { int sensor = bytes[1] & 0xff; int value = (bytes[3] & 0xff) << 8 | (bytes[2] & 0xff); return (T) new GetValueRes(sensor, value); } 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) { return (T) new GetSensorNameRes(bytes); } throw new RuntimeException("Unknown code: " + c); } public static class GetSensorCountRes { public final int count; public GetSensorCountRes(int count) { this.count = count; } } public static class GetValueRes { public final int sensor; public final int value; public GetValueRes(int sensor, int value) { this.sensor = sensor; this.value = value; } } 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 }; } 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(8), 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; } } }