1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-10-10 15:18:13 +02:00

Retrieve steps count

Tried to set weather, but without success
Handle ACK response
Enabled alarms
This commit is contained in:
mkusnierz 2019-10-18 23:36:56 +02:00
parent 290a90ec0e
commit 34d9bccb86
3 changed files with 106 additions and 12 deletions

View File

@ -54,7 +54,9 @@ public final class WatchXPlusConstants {
public static final byte[] CMD_AUTHORIZATION_TASK = new byte[]{0x01, 0x05};
public static final byte[] CMD_TIME_SETTINGS = new byte[]{0x01, 0x08};
public static final byte[] CMD_ALARM_SETTINGS = new byte[]{0x01, 0x0A};
public static final byte[] CMD_WEATHER_SET = new byte[]{0x01, 0x10};
public static final byte[] CMD_BATTERY_INFO = new byte[]{0x01, 0x14};
public static final byte[] CMD_HEARTRATE_INFO = new byte[]{0x15, 0x03};
public static final byte[] CMD_NOTIFICATION_TASK = new byte[]{0x03, 0x01};
public static final byte[] CMD_NOTIFICATION_TEXT_TASK = new byte[]{0x03, 0x06};
@ -65,15 +67,18 @@ public final class WatchXPlusConstants {
public static final byte[] CMD_DO_NOT_DISTURB_SETTINGS = new byte[]{0x03, 0x61};
public static final byte[] CMD_FITNESS_GOAL_SETTINGS = new byte[]{0x10, 0x02};
public static final byte[] CMD_DAY_STEPS_INFO = new byte[]{0x10, 0x03};
public static final byte[] RESP_AUTHORIZATION_TASK = new byte[]{0x01, 0x01, 0x05};
public static final byte[] RESP_BUTTON_INDICATOR = new byte[]{0x04, 0x03, 0x11};
public static final byte[] RESP_ALARM_INDICATOR = new byte[]{-0x80, 0x01, 0x0A};
public static final byte[] RESP_DAY_STEPS_INDICATOR = new byte[]{0x08, 0x10, 0x03};
public static final byte[] RESP_HEARTRATE = new byte[]{0x08, 0x15, 0x02};
public static final byte[] RESP_FIRMWARE_INFO = new byte[]{0x08, 0x01, 0x02};
public static final byte[] RESP_TIME_SETTINGS = new byte[]{0x08, 0x01, 0x08};
public static final byte[] RESP_BATTERY_INFO = new byte[]{0x08, 0x01, 0x14};
public static final byte[] RESP_NOTIFICATION_SETTINGS = new byte[]{0x08, 0x03, 0x02};
public static final byte[] RESP_NOTIFICATION_SETTINGS = new byte[]{0x01, 0x03, 0x02};
public static final String ACTION_ENABLE = "action.watch9.enable";

View File

@ -8,17 +8,16 @@ import android.net.Uri;
import android.os.Build;
import android.os.ParcelUuid;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Collection;
import java.util.Collections;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.watchxplus.WatchXPlusConstants;
import nodomain.freeyourgadget.gadgetbridge.devices.watchxplus.WatchXPlusPairingActivity;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
@ -76,7 +75,7 @@ public class WatchXPlusDeviceCoordinator extends AbstractDeviceCoordinator {
@Override
public boolean supportsActivityDataFetching() {
return false;
return true;
}
@Override
@ -101,7 +100,7 @@ public class WatchXPlusDeviceCoordinator extends AbstractDeviceCoordinator {
@Override
public int getAlarmSlotCount() {
return 0;
return 3;
}
@Override
@ -111,7 +110,7 @@ public class WatchXPlusDeviceCoordinator extends AbstractDeviceCoordinator {
@Override
public boolean supportsHeartRateMeasurement(GBDevice device) {
return false;
return true;
}
@Override
@ -141,7 +140,7 @@ public class WatchXPlusDeviceCoordinator extends AbstractDeviceCoordinator {
@Override
public boolean supportsWeather() {
return false;
return true;
}
@Override

View File

@ -478,6 +478,22 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport {
@Override
public void onFetchRecordedData(int dataTypes) {
TransactionBuilder builder = null;
try {
builder = performInitialized("fetchData");
builder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE),
buildCommand(WatchXPlusConstants.CMD_DAY_STEPS_INFO,
WatchXPlusConstants.READ_VALUE));
// TODO: Watch does not return heart rate data after this command. Check why
// builder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE),
// buildCommand(WatchXPlusConstants.CMD_HEARTRATE_INFO,
// WatchXPlusConstants.READ_VALUE));
performImmediately(builder);
} catch (IOException e) {
LOG.warn("Unable to retrieve recorded data", e);
}
}
@Override
@ -558,7 +574,26 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport {
@Override
public void onSendWeather(WeatherSpec weatherSpec) {
try {
TransactionBuilder builder = performInitialized("setWeather");
byte[] command = WatchXPlusConstants.CMD_WEATHER_SET;
byte[] weatherInfo = new byte[5];
// bArr[8] = (byte) (this.mWeatherType >> 8);
// bArr[9] = (byte) this.mWeatherType;
// bArr[10] = (byte) this.mLowTemp;
// bArr[11] = (byte) this.mHighTemp;
// bArr[12] = (byte) this.mCurrentTemp;
builder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE),
buildCommand(command,
WatchXPlusConstants.KEEP_ALIVE,
weatherInfo));
performImmediately(builder);
} catch (IOException e) {
LOG.warn("Unable to set time", e);
}
}
@Override
@ -576,23 +611,71 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport {
} else if (ArrayUtils.equals(value, WatchXPlusConstants.RESP_TIME_SETTINGS, 5)) {
handleTime(value);
} else if (ArrayUtils.equals(value, WatchXPlusConstants.RESP_BUTTON_INDICATOR, 5)) {
LOG.info("Unhandled action: Button pressed");
// It looks like WatchXPlus doesn't send this action
LOG.info(" Unhandled action: Button pressed");
} else if (ArrayUtils.equals(value, WatchXPlusConstants.RESP_ALARM_INDICATOR, 5)) {
LOG.info("Alarm active: id=" + value[8]);
LOG.info(" Alarm active: id=" + value[8]);
} else if (isCalibrationActive && value.length == 7 && value[4] == ACK_CALIBRATION) {
setTime(BLETypeConversions.createCalendar());
isCalibrationActive = false;
} else if (ArrayUtils.equals(value, WatchXPlusConstants.RESP_DAY_STEPS_INDICATOR, 5)) {
handleStepsInfo(value);
} else if (ArrayUtils.equals(value, WatchXPlusConstants.RESP_HEARTRATE, 5)) {
LOG.info(" Received Heart rate history");
} else if (value.length == 7 && value[5] == 0) {
// Not sure if that's necessary
handleAck();
} else if (ArrayUtils.equals(value, WatchXPlusConstants.RESP_NOTIFICATION_SETTINGS, 5)) {
LOG.info(" Received notification settings status");
} else {
LOG.info(" Unhandled value change for characteristic: " + characteristicUUID);
logMessageContent(characteristic.getValue());
}
return true;
} else {
LOG.info("Unhandled characteristic changed: " + characteristicUUID);
LOG.info(" Unhandled characteristic changed: " + characteristicUUID);
logMessageContent(characteristic.getValue());
}
return false;
}
private void handleAck() {
try {
TransactionBuilder builder = performInitialized("handleAck");
builder.write(getCharacteristic(WatchXPlusConstants.UUID_CHARACTERISTIC_WRITE),
// TODO: Below value is ACK status. Find out which value is correct
buildCommand((byte)0x00));
performImmediately(builder);
} catch (IOException e) {
LOG.warn("Unable to response to ACK", e);
}
}
// This is only for ACK response
private byte[] buildCommand(byte action) {
byte[] result = new byte[7];
System.arraycopy(WatchXPlusConstants.CMD_HEADER, 0, result, 0, 5);
result[2] = (byte) (result.length + 1);
result[3] = WatchXPlusConstants.REQUEST;
result[4] = (byte) sequenceNumber++;
result[5] = action;
result[result.length - 1] = calculateChecksum(result);
return result;
}
private void handleStepsInfo(byte[] value) {
int steps = Conversion.fromByteArr16(value[8], value[9]);
if (LOG.isDebugEnabled()) {
LOG.debug(" Received steps count: " + steps);
}
// TODO: save steps to DB
}
private byte[] buildCommand(byte[] command, byte action) {
return buildCommand(command, action, null);
}
@ -656,6 +739,13 @@ public class WatchXPlusDeviceSupport extends AbstractBTLEDeviceSupport {
static byte[] toByteArr16(int value) {
return new byte[]{(byte) (value >> 8), (byte) value};
}
static int fromByteArr16(byte... value) {
int intValue = 0;
for (int i2 = 0; i2 < value.length; i2++) {
intValue += (value[i2] & 255) << (((value.length - 1) - i2) * 8);
}
return intValue;
}
static byte[] toByteArr32(int value) {
return new byte[]{(byte) (value >> 24),