1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-03 11:33:19 +02:00

Lefun: Add command serializers

This commit is contained in:
Yukai Li 2020-09-30 22:32:13 -06:00 committed by Gitea
parent fd56db55c4
commit 265260810a
29 changed files with 2460 additions and 0 deletions

View File

@ -23,9 +23,52 @@ import java.util.UUID;
import static nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport.BASE_UUID;
public class LefunConstants {
// BLE UUIDs
public static final UUID UUID_SERVICE_LEFUN = UUID.fromString(String.format(BASE_UUID, "18D0"));
// Coordinator constants
public static final String ADVERTISEMENT_NAME = "Lefun";
public static final String MANUFACTURER_NAME = "Teng Jin Da";
public static int NUM_ALARM_SLOTS = 5;
// Commands
public static final byte CMD_REQUEST_ID = (byte)0xab;
public static final byte CMD_RESPONSE_ID = 0x55;
public static final int CMD_MAX_LENGTH = 20;
// 3 header bytes plus checksum
public static final int CMD_HEADER_LENGTH = 4;
public static final byte CMD_FIRMWARE_INFO = 0x00;
public static final byte CMD_BONDING_REQUEST = 0x01;
public static final byte CMD_SETTINGS = 0x02;
public static final byte CMD_BATTERY_LEVEL = 0x03;
public static final byte CMD_TIME = 0x04;
public static final byte CMD_ALARM = 0x05;
public static final byte CMD_PROFILE = 0x06;
public static final byte CMD_UI_PAGES = 0x07;
public static final byte CMD_FEATURES = 0x08;
public static final byte CMD_FIND_DEVICE = 0x09;
public static final byte CMD_FIND_PHONE = 0x0a;
public static final byte CMD_SEDENTARY_REMINDER_INTERVAL = 0x0b;
public static final byte CMD_HYDRATION_REMINDER_INTERVAL = 0x0c;
public static final byte CMD_REMOTE_CAMERA = 0x0d;
public static final byte CMD_REMOTE_CAMERA_TRIGGERED = 0x0e;
public static final byte CMD_PPG_START = 0x0f;
public static final byte CMD_PPG_RESULT = 0x10;
public static final byte CMD_PPG_DATA = 0x11;
public static final byte CMD_STEPS_DATA = 0x12;
public static final byte CMD_ACTIVITY_DATA = 0x13;
public static final byte CMD_SLEEP_TIME_DATA = 0x14;
public static final byte CMD_SLEEP_DATA = 0x15;
public static final byte CMD_NOTIFICATION = 0x17;
public static final byte CMD_LANGUAGE = 0x21;
public static final byte CMD_UNKNOWN_22 = 0x22;
public static final byte CMD_UNKNOWN_25 = 0x25;
public static final byte CMD_UNKNOWN_80 = (byte)0x80;
public static final int PPG_TYPE_INVALID = -1;
public static final int PPG_TYPE_HEART_RATE = 0;
public static final int PPG_TYPE_BLOOD_PRESSURE = 1;
public static final int PPG_TYPE_BLOOD_OXYGEN = 2;
}

View File

@ -0,0 +1,41 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun;
public class LefunFeatureSupport {
public static final int SUPPORT_HEART_RATE = 1 << 2;
public static final int SUPPORT_BLOOD_PRESSURE = 1 << 3;
public static final int SUPPORT_FAKE_ECG = 1 << 10;
public static final int SUPPORT_ECG = 1 << 11;
public static final int SUPPORT_WALLPAPER_UPLOAD = 1 << 12;
public static final int RESERVE_BLOOD_OXYGEN = 1 << 0;
public static final int RESERVE_CLOCK_FACE_UPLOAD = 1 << 3;
public static final int RESERVE_CONTACTS = 1 << 5;
public static final int RESERVE_WALLPAPER = 1 << 6;
public static final int RESERVE_REMOTE_CAMERA = 1 << 7;
public static boolean checkSupported(short deviceSupport, int featureSupport) {
return (deviceSupport & featureSupport) == featureSupport;
}
public static boolean checkNotReserved(short deviceReserve, int featureReserve) {
return !((deviceReserve & featureReserve) == featureReserve);
}
}

View File

@ -0,0 +1,174 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class AlarmCommand extends BaseCommand {
public static final int DOW_SUNDAY = 0;
public static final int DOW_MONDAY = 1;
public static final int DOW_TUESDAY = 2;
public static final int DOW_WEDNESDAY = 3;
public static final int DOW_THURSDAY = 4;
public static final int DOW_FRIDAY = 5;
public static final int DOW_SATURDAY = 6;
private byte op;
private byte index;
private boolean enabled;
// Snooze is not implemented how you think it would be
// Number of snoozes is decremented every time the alarm triggers, and the alarm time
// is moved forward by number of minutes in snooze time. It never gets reset to the
// original time.
private byte numOfSnoozes;
private byte snoozeTime;
private byte dayOfWeek;
private byte hour;
private byte minute;
private boolean setSuccess;
public byte getOp() {
return op;
}
public void setOp(byte op) {
if (op != OP_GET && op != OP_SET)
throw new IllegalArgumentException("Operation must be get or set");
this.op = op;
}
public byte getIndex() {
return index;
}
public void setIndex(byte index) {
if (index < 0 || index >= LefunConstants.NUM_ALARM_SLOTS)
throw new IllegalArgumentException("Index must be between 0 and "
+ (LefunConstants.NUM_ALARM_SLOTS - 1) + " inclusive");
this.index = index;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public byte getNumOfSnoozes() {
return numOfSnoozes;
}
public void setNumOfSnoozes(byte numOfSnoozes) {
this.numOfSnoozes = numOfSnoozes;
}
public byte getSnoozeTime() {
return snoozeTime;
}
public void setSnoozeTime(byte snoozeTime) {
this.snoozeTime = snoozeTime;
}
public boolean getDayOfWeek(int day) {
if (day < 0 || day > 6)
throw new IllegalArgumentException("Invalid day of week");
return getBit(dayOfWeek, 1 << day);
}
public void setDayOfWeek(int day, boolean enabled) {
if (day < 0 || day > 6)
throw new IllegalArgumentException("Invalid day of week");
dayOfWeek = setBit(dayOfWeek, 1 << day, enabled);
}
public byte getHour() {
return hour;
}
public void setHour(byte hour) {
if (hour < 0 || hour > 23)
throw new IllegalArgumentException("Hour must be between 0 and 23 inclusive");
this.hour = hour;
}
public byte getMinute() {
return minute;
}
public void setMinute(byte minute) {
if (minute < 0 || minute > 59)
throw new IllegalArgumentException("Minute must be between 0 and 59 inclusive");
this.minute = minute;
}
public boolean isSetSuccess() {
return setSuccess;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateId(id, LefunConstants.CMD_ALARM);
if (params.limit() < 2)
throwUnexpectedLength();
op = params.get();
index = params.get();
if (op == OP_GET) {
if (params.limit() != 8)
throwUnexpectedLength();
enabled = params.get() == 1;
numOfSnoozes = params.get();
snoozeTime = params.get();
dayOfWeek = params.get();
hour = params.get();
minute = params.get();
} else if (op == OP_SET) {
if (params.limit() != 3)
throwUnexpectedLength();
setSuccess = params.get() == 1;
} else {
throw new IllegalArgumentException("Invalid operation type received");
}
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(op);
// No alarm ID for get; all of them are returned
if (op == OP_SET) {
params.put(index);
params.put((byte)(enabled ? 1: 0));
params.put(numOfSnoozes);
params.put(snoozeTime);
params.put(dayOfWeek);
params.put(hour);
params.put(minute);
}
return LefunConstants.CMD_ALARM;
}
}

View File

@ -0,0 +1,148 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public abstract class BaseCommand {
// Common constants
public static final byte OP_GET = 0;
public static final byte OP_SET = 1;
abstract protected void deserializeParams(byte id, ByteBuffer params);
abstract protected byte serializeParams(ByteBuffer params);
public void deserialize(byte[] response) {
if (response.length < LefunConstants.CMD_HEADER_LENGTH)
throw new IllegalArgumentException("Response is too short");
if (calculateChecksum(response, 0, response.length - 1) != response[response.length - 1])
throw new IllegalArgumentException("Incorrect message checksum");
ByteBuffer buffer = ByteBuffer.wrap(response, LefunConstants.CMD_HEADER_LENGTH - 1,
response.length - LefunConstants.CMD_HEADER_LENGTH);
buffer.order(ByteOrder.BIG_ENDIAN);
deserializeParams(response[2], buffer);
}
public byte[] serialize() {
ByteBuffer buffer = ByteBuffer.allocate(LefunConstants.CMD_MAX_LENGTH - LefunConstants.CMD_HEADER_LENGTH);
buffer.order(ByteOrder.BIG_ENDIAN);
byte id = serializeParams(buffer);
return makeCommand(id, buffer);
}
static protected byte calculateChecksum(byte[] data, int offset, int length) {
byte checksum = 0;
for (int i = offset; i < offset + length; ++i) {
byte b = data[i];
for (int j = 0; j < 8; ++j) {
if (((b ^ checksum) & 1) == 0) {
checksum >>= 1;
} else {
checksum = (byte) ((checksum ^ 0x18) >> 1 | 0x80);
}
b >>= 1;
}
}
return checksum;
}
/**
* Builds a command given ID and parameters buffer.
*
* @param id the command ID
* @param params the parameters buffer
* @return the assembled command buffer
*/
protected byte[] makeCommand(byte id, ByteBuffer params) {
if (params.position() > LefunConstants.CMD_MAX_LENGTH - LefunConstants.CMD_HEADER_LENGTH)
throw new IllegalArgumentException("params is too long to fit");
int paramsLength = params.position();
byte[] request = new byte[paramsLength + LefunConstants.CMD_HEADER_LENGTH];
request[0] = LefunConstants.CMD_REQUEST_ID;
request[1] = (byte) request.length;
request[2] = id;
params.flip();
params.get(request, LefunConstants.CMD_HEADER_LENGTH - 1, paramsLength);
request[request.length - 1] = calculateChecksum(request, 0, request.length - 1);
return request;
}
protected void throwUnexpectedLength() {
throw new IllegalArgumentException("Unexpected parameters length");
}
protected void validateId(byte id, byte expectedId) {
if (id != expectedId)
throw new IllegalArgumentException("Wrong command ID");
}
protected void validateIdAndLength(byte id, ByteBuffer params, byte expectedId, int expectedLength) {
validateId(id, expectedId);
if (params.limit() != expectedLength)
throwUnexpectedLength();
}
protected boolean getBit(int value, int mask) {
return (value & mask) != 0;
}
protected int setBit(int value, int mask, boolean set) {
if (set) {
return value | mask;
} else {
return value & ~mask;
}
}
protected short setBit(short value, int mask, boolean set) {
if (set) {
return (short)(value | mask);
} else {
return (short)(value & ~mask);
}
}
protected byte setBit(byte value, int mask, boolean set) {
if (set) {
return (byte)(value | mask);
} else {
return (byte)(value & ~mask);
}
}
/**
* Find index of first bit that is set
* @param value
* @return
*/
protected int getLowestSetBitIndex(int value) {
if (value == 0) return -1;
int i = 0;
while ((value & 1) == 0) {
++i;
value >>= 1;
}
return i;
}
}

View File

@ -0,0 +1,84 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class Cmd22Command extends BaseCommand {
private byte op;
private short unknown;
private boolean setSuccess;
public byte getOp() {
return op;
}
public void setOp(byte op) {
if (op != OP_GET && op != OP_SET)
throw new IllegalArgumentException("Operation must be get or set");
this.op = op;
}
public short getUnknown() {
return unknown;
}
public void setUnknown(short unknown) {
this.unknown = unknown;
}
public boolean isSetSuccess() {
return setSuccess;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateId(id, LefunConstants.CMD_UNKNOWN_22);
if (params.limit() < 1)
throwUnexpectedLength();
op = params.get();
if (op == OP_GET) {
if (params.limit() != 3)
throwUnexpectedLength();
unknown = params.getShort();
} else if (op == OP_SET) {
if (params.limit() != 2)
throwUnexpectedLength();
setSuccess = params.get() == 1;
} else {
throw new IllegalArgumentException("Invalid operation type received");
}
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(op);
if (op == OP_SET) {
params.putShort(unknown);
}
return LefunConstants.CMD_UNKNOWN_22;
}
}

View File

@ -0,0 +1,86 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class Cmd25Command extends BaseCommand {
private byte op;
private int unknown;
private boolean setSuccess;
public byte getOp() {
return op;
}
public void setOp(byte op) {
if (op != OP_GET && op != OP_SET)
throw new IllegalArgumentException("Operation must be get or set");
this.op = op;
}
public int getUnknown() {
return unknown;
}
public void setUnknown(int unknown) {
if (unknown < 0 || unknown > 65000)
throw new IllegalArgumentException("Value must be between 0 and 65000 inclusive");
this.unknown = unknown;
}
public boolean isSetSuccess() {
return setSuccess;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateId(id, LefunConstants.CMD_UNKNOWN_25);
if (params.limit() < 1)
throwUnexpectedLength();
op = params.get();
if (op == OP_GET) {
if (params.limit() != 5)
throwUnexpectedLength();
unknown = params.getInt();
} else if (op == OP_SET) {
if (params.limit() != 2)
throwUnexpectedLength();
setSuccess = params.get() == 1;
} else {
throw new IllegalArgumentException("Invalid operation type received");
}
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(op);
if (op == OP_SET) {
params.putInt(unknown);
}
return LefunConstants.CMD_UNKNOWN_25;
}
}

View File

@ -0,0 +1,95 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class FeaturesCommand extends BaseCommand {
public static final int FEATURE_RAISE_TO_WAKE = 0;
public static final int FEATURE_SEDENTARY_REMINDER = 1;
public static final int FEATURE_HYDRATION_REMINDER = 2;
public static final int FEATURE_REMOTE_CAMERA = 3;
public static final int FEATURE_UNKNOWN_4 = 4;
public static final int FEATURE_ANTI_LOST = 5;
private byte op;
private short features;
private boolean setSuccess;
public byte getOp() {
return op;
}
public void setOp(byte op) {
if (op != OP_GET && op != OP_SET)
throw new IllegalArgumentException("Operation must be get or set");
this.op = op;
}
public boolean getFeature(int index) {
if (index < 0 || index > 5)
throw new IllegalArgumentException("Index must be between 0 and 5 inclusive");
return getBit(features, 1 << index);
}
public void setFeature(int index, boolean enabled) {
if (index < 0 || index > 5)
throw new IllegalArgumentException("Index must be between 0 and 5 inclusive");
features = setBit(features, 1 << index, enabled);
}
public boolean isSetSuccess() {
return setSuccess;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateId(id, LefunConstants.CMD_FEATURES);
if (params.limit() < 1)
throwUnexpectedLength();
op = params.get();
if (op == OP_GET) {
if (params.limit() != 3)
throwUnexpectedLength();
features = params.getShort();
} else if (op == OP_SET) {
if (params.limit() != 2)
throwUnexpectedLength();
setSuccess = params.get() == 1;
} else {
throw new IllegalArgumentException("Invalid operation type received");
}
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(op);
if (op == OP_SET) {
params.putShort(features);
}
return LefunConstants.CMD_FEATURES;
}
}

View File

@ -0,0 +1,43 @@
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class FindDeviceCommand extends BaseCommand {
private boolean success;
public boolean isSuccess() {
return success;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateIdAndLength(id, params, LefunConstants.CMD_FIND_DEVICE, 1);
success = params.get() == 1;
}
@Override
protected byte serializeParams(ByteBuffer params) {
return LefunConstants.CMD_FIND_DEVICE;
}
}

View File

@ -0,0 +1,35 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class FindPhoneCommand extends BaseCommand {
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateIdAndLength(id, params, LefunConstants.CMD_FIND_PHONE, 0);
}
@Override
protected byte serializeParams(ByteBuffer params) {
return LefunConstants.CMD_FIND_PHONE;
}
}

View File

@ -0,0 +1,110 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class GetActivityDataCommand extends BaseCommand {
private byte daysAgo;
private byte totalRecords;
private byte currentRecord;
private byte year;
private byte month;
private byte day;
private byte hour;
private byte minute;
private short steps;
private short distance; // m
private short calories; // calories
public byte getDaysAgo() {
return daysAgo;
}
public void setDaysAgo(byte daysAgo) {
if (daysAgo < 0 || daysAgo > 6)
throw new IllegalArgumentException("Days ago must be between 0 and 6 inclusive");
this.daysAgo = daysAgo;
}
public byte getTotalRecords() {
return totalRecords;
}
public byte getCurrentRecord() {
return currentRecord;
}
public byte getYear() {
return year;
}
public byte getMonth() {
return month;
}
public byte getDay() {
return day;
}
public byte getHour() {
return hour;
}
public byte getMinute() {
return minute;
}
public short getSteps() {
return steps;
}
public short getDistance() {
return distance;
}
public short getCalories() {
return calories;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateIdAndLength(id, params, LefunConstants.CMD_ACTIVITY_DATA, 14);
daysAgo = params.get();
totalRecords = params.get();
currentRecord = params.get();
year = params.get();
month = params.get();
day = params.get();
hour = params.get();
minute = params.get();
steps = params.getShort();
distance = params.getShort();
calories = params.getShort();
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(daysAgo);
return LefunConstants.CMD_ACTIVITY_DATA;
}
}

View File

@ -0,0 +1,43 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class GetBatteryLevelCommand extends BaseCommand {
private byte batteryLevel;
public byte getBatteryLevel() {
return batteryLevel;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateIdAndLength(id, params, LefunConstants.CMD_BATTERY_LEVEL, 1);
batteryLevel = params.get();
}
@Override
protected byte serializeParams(ByteBuffer params) {
return LefunConstants.CMD_BATTERY_LEVEL;
}
}

View File

@ -0,0 +1,78 @@
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
import java.nio.charset.StandardCharsets;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class GetFirmwareInfoCommand extends BaseCommand {
private short supportCode;
private short devTypeReserveCode;
private String typeCode;
private short hardwareVersion;
private short softwareVersion;
private String vendorCode;
public short getSupportCode() {
return supportCode;
}
public short getDevTypeReserveCode() {
return devTypeReserveCode;
}
public String getTypeCode() {
return typeCode;
}
public short getHardwareVersion() {
return hardwareVersion;
}
public short getSoftwareVersion() {
return softwareVersion;
}
public String getVendorCode() {
return vendorCode;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateIdAndLength(id, params, LefunConstants.CMD_FIRMWARE_INFO, 16);
supportCode = (short)(params.get() | (params.get() << 8));
devTypeReserveCode = params.getShort();
byte[] typeCodeBytes = new byte[4];
params.get(typeCodeBytes);
typeCode = new String(typeCodeBytes, StandardCharsets.US_ASCII);
hardwareVersion = params.getShort();
softwareVersion = params.getShort();
byte[] vendorCodeBytes = new byte[4];
params.get(vendorCodeBytes);
vendorCode = new String(vendorCodeBytes, StandardCharsets.US_ASCII);
}
@Override
protected byte serializeParams(ByteBuffer params) {
return LefunConstants.CMD_FIRMWARE_INFO;
}
}

View File

@ -0,0 +1,118 @@
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class GetPpgDataCommand extends BaseCommand {
private byte ppgType;
private short totalRecords;
private short currentRecord;
private byte year;
private byte month;
private byte day;
private byte hour;
private byte minute;
private byte second;
private byte[] ppgData;
public int getPpgType() {
return getLowestSetBitIndex(ppgType);
}
public void setPpgType(int type) {
if (type < 0 || type > 2)
throw new IllegalArgumentException("Invalid PPG type");
this.ppgType = (byte)(1 << type);
}
public short getTotalRecords() {
return totalRecords;
}
public short getCurrentRecord() {
return currentRecord;
}
public byte getYear() {
return year;
}
public byte getMonth() {
return month;
}
public byte getDay() {
return day;
}
public byte getHour() {
return hour;
}
public byte getMinute() {
return minute;
}
public byte getSecond() {
return second;
}
public byte[] getPpgData() {
return ppgData;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateId(id, LefunConstants.CMD_PPG_DATA);
if (params.limit() < 9)
throwUnexpectedLength();
ppgType = params.get();
totalRecords = params.get();
currentRecord = params.get();
year = params.get();
month = params.get();
day = params.get();
hour = params.get();
minute = params.get();
second = params.get();
int typeIndex = getPpgType();
int dataLength;
switch (typeIndex) {
case LefunConstants.PPG_TYPE_HEART_RATE:
case LefunConstants.PPG_TYPE_BLOOD_OXYGEN:
dataLength = 1;
break;
case LefunConstants.PPG_TYPE_BLOOD_PRESSURE:
dataLength = 2;
break;
default:
throw new IllegalArgumentException("Unknown PPG type");
}
if (params.limit() < dataLength + 9)
throwUnexpectedLength();
ppgData = new byte[dataLength];
params.get(ppgData);
// Extended count/index
if (params.limit() == dataLength + 11)
{
totalRecords |= params.get() << 8;
currentRecord |= params.get() << 8;
}
else if (params.limit() > dataLength + 11) {
throwUnexpectedLength();
}
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(ppgType);
return LefunConstants.CMD_PPG_DATA;
}
}

View File

@ -0,0 +1,102 @@
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class GetSleepDataCommand extends BaseCommand {
public static final int SLEEP_TYPE_AWAKE = 1;
public static final int SLEEP_TYPE_LIGHT_SLEEP = 2;
public static final int SLEEP_TYPE_DEEP_SLEEP = 3;
private byte daysAgo;
private short totalRecords;
private short currentRecord;
private byte year;
private byte month;
private byte day;
private byte hour;
private byte minute;
private byte sleepType;
public byte getDaysAgo() {
return daysAgo;
}
public void setDaysAgo(byte daysAgo) {
if (daysAgo < 0 || daysAgo > 6)
throw new IllegalArgumentException("Days ago must be between 0 and 6 inclusive");
this.daysAgo = daysAgo;
}
public short getTotalRecords() {
return totalRecords;
}
public short getCurrentRecord() {
return currentRecord;
}
public byte getYear() {
return year;
}
public byte getMonth() {
return month;
}
public byte getDay() {
return day;
}
public byte getHour() {
return hour;
}
public byte getMinute() {
return minute;
}
public byte getSleepType() {
return sleepType;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateIdAndLength(id, params, LefunConstants.CMD_SLEEP_DATA, 11);
daysAgo = params.get();
totalRecords = params.getShort();
currentRecord = params.getShort();
year = params.get();
month = params.get();
day = params.get();
hour = params.get();
minute = params.get();
sleepType = params.get();
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(daysAgo);
return LefunConstants.CMD_SLEEP_DATA;
}
}

View File

@ -0,0 +1,75 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class GetSleepTimeCommand extends BaseCommand {
private byte daysAgo;
private byte year;
private byte month;
private byte day;
private short minutes;
public byte getDaysAgo() {
return daysAgo;
}
public void setDaysAgo(byte daysAgo) {
if (daysAgo < 0 || daysAgo > 6)
throw new IllegalArgumentException("Days ago must be between 0 and 6 inclusive");
this.daysAgo = daysAgo;
}
public byte getYear() {
return year;
}
public byte getMonth() {
return month;
}
public byte getDay() {
return day;
}
public short getMinutes() {
return minutes;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateIdAndLength(id, params, LefunConstants.CMD_SLEEP_TIME_DATA, 6);
daysAgo = params.get();
year = params.get();
month = params.get();
day = params.get();
minutes = params.getShort();
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(daysAgo);
return LefunConstants.CMD_SLEEP_TIME_DATA;
}
}

View File

@ -0,0 +1,86 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class GetStepsDataCommand extends BaseCommand {
private byte daysAgo;
private byte year;
private byte month;
private byte day;
private int steps;
private int distance; // m
private int calories; // calories
public byte getDaysAgo() {
return daysAgo;
}
public void setDaysAgo(byte daysAgo) {
if (daysAgo < 0 || daysAgo > 6)
throw new IllegalArgumentException("Days ago must be between 0 and 6 inclusive");
this.daysAgo = daysAgo;
}
public byte getYear() {
return year;
}
public byte getMonth() {
return month;
}
public byte getDay() {
return day;
}
public int getSteps() {
return steps;
}
public int getDistance() {
return distance;
}
public int getCalories() {
return calories;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateIdAndLength(id, params, LefunConstants.CMD_STEPS_DATA, 16);
daysAgo = params.get();
year = params.get();
month = params.get();
day = params.get();
steps = params.getInt();
distance = params.getInt();
calories = params.getInt();
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(daysAgo);
return LefunConstants.CMD_STEPS_DATA;
}
}

View File

@ -0,0 +1,86 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class HydrationReminderIntervalCommand extends BaseCommand {
private byte op;
private byte hydrationReminderInterval;
private boolean setSuccess;
public byte getOp() {
return op;
}
public void setOp(byte op) {
if (op != OP_GET && op != OP_SET)
throw new IllegalArgumentException("Operation must be get or set");
this.op = op;
}
public byte getHydrationReminderInterval() {
return hydrationReminderInterval;
}
public void setHydrationReminderInterval(byte hydrationReminderInterval) {
if (hydrationReminderInterval == 0)
throw new IllegalArgumentException("Interval must be non-zero");
this.hydrationReminderInterval = hydrationReminderInterval;
}
public boolean isSetSuccess() {
return setSuccess;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateId(id, LefunConstants.CMD_HYDRATION_REMINDER_INTERVAL);
if (params.limit() < 1)
throwUnexpectedLength();
op = params.get();
if (op == OP_GET) {
if (params.limit() != 2)
throwUnexpectedLength();
hydrationReminderInterval = params.get();
} else if (op == OP_SET) {
if (params.limit() != 2)
throwUnexpectedLength();
setSuccess = params.get() == 1;
} else {
throw new IllegalArgumentException("Invalid operation type received");
}
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(op);
if (op == OP_SET) {
params.put(hydrationReminderInterval);
}
return LefunConstants.CMD_HYDRATION_REMINDER_INTERVAL;
}
}

View File

@ -0,0 +1,121 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class NotificationCommand extends BaseCommand {
public static final byte SERVICE_TYPE_CALL = 0;
public static final byte SERVICE_TYPE_TEXT = 1;
public static final byte SERVICE_TYPE_QQ = 2;
public static final byte SERVICE_TYPE_WECHAT = 3;
public static final byte SERVICE_TYPE_EXTENDED = 4;
public static final byte EXTENDED_SERVICE_TYPE_FACEBOOK = 1;
public static final byte EXTENDED_SERVICE_TYPE_TWITTER = 2;
public static final byte EXTENDED_SERVICE_TYPE_LINKEDIN = 3;
public static final byte EXTENDED_SERVICE_TYPE_WHATSAPP = 4;
public static final byte EXTENDED_SERVICE_TYPE_LINE = 5;
public static final byte EXTENDED_SERVICE_TYPE_KAKAOTALK = 6;
private byte serviceType;
private byte totalPieces;
private byte currentPiece;
private byte extendedServiceType;
private byte[] payload;
public int getServiceType() {
return getLowestSetBitIndex(serviceType);
}
public void setServiceType(int type) {
if (type < 0 || type > 4)
throw new IllegalArgumentException("Invalid service type");
this.serviceType = (byte)(1 << type);
}
public byte getTotalPieces() {
return totalPieces;
}
public void setTotalPieces(byte totalPieces) {
// This check isn't on device, but should probably be added
if (totalPieces == 0)
throw new IllegalArgumentException("Total pieces must not be 0");
this.totalPieces = totalPieces;
}
public byte getCurrentPiece() {
return currentPiece;
}
public void setCurrentPiece(byte currentPiece) {
// This check isn't on device, but should probably be added
if (currentPiece == 0)
throw new IllegalArgumentException("Current piece must not be 0");
this.currentPiece = currentPiece;
}
public byte getExtendedServiceType() {
return extendedServiceType;
}
public void setExtendedServiceType(byte extendedServiceType) {
this.extendedServiceType = extendedServiceType;
}
public byte[] getPayload() {
return payload;
}
public void setPayload(byte[] payload) {
if (payload == null)
throw new IllegalArgumentException("Payload must not be null");
if (payload.length > 13)
throw new IllegalArgumentException("Payload is too long");
this.payload = payload;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
// We should not receive a response for this
throw new UnsupportedOperationException();
}
@Override
protected byte serializeParams(ByteBuffer params) {
boolean hasExtendedServiceType = (serviceType & (1 << SERVICE_TYPE_EXTENDED)) != 0
&& (extendedServiceType & 0x0f) != 0 ;
int maxPayloadLength = hasExtendedServiceType ? 12 : 13;
if (payload.length > maxPayloadLength)
throw new IllegalStateException("Payload is too long");
params.put(serviceType);
params.put(totalPieces);
params.put(currentPiece);
if (hasExtendedServiceType)
params.put(extendedServiceType);
params.put(payload);
return LefunConstants.CMD_NOTIFICATION;
}
}

View File

@ -0,0 +1,72 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class PpgResultCommand extends BaseCommand {
private byte ppgType;
private byte[] ppgData;
public int getPpgType() {
return getLowestSetBitIndex(ppgType);
}
public byte[] getPpgData() {
return ppgData;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateId(id, LefunConstants.CMD_PPG_RESULT);
if (params.limit() < 1)
throwUnexpectedLength();
ppgType = params.get();
int typeIndex = getPpgType();
int dataLength;
switch (typeIndex) {
case LefunConstants.PPG_TYPE_HEART_RATE:
case LefunConstants.PPG_TYPE_BLOOD_OXYGEN:
dataLength = 1;
break;
case LefunConstants.PPG_TYPE_BLOOD_PRESSURE:
dataLength = 2;
break;
default:
throw new IllegalArgumentException("Unknown PPG type");
}
if (params.limit() != dataLength + 1)
throwUnexpectedLength();
ppgData = new byte[dataLength];
params.get(ppgData);
}
@Override
protected byte serializeParams(ByteBuffer params) {
// No handler on device side
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,130 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class ProfileCommand extends BaseCommand {
public static final byte GENDER_FEMALE = 0;
public static final byte GENDER_MALE = 1;
private byte op;
private byte gender;
private byte height; // cm
private byte weight; // kg
private byte age; // years
private boolean setSuccess;
public byte getOp() {
return op;
}
public void setOp(byte op) {
if (op != OP_GET && op != OP_SET)
throw new IllegalArgumentException("Operation must be get or set");
this.op = op;
}
public byte getGender() {
return gender;
}
public void setGender(byte gender) {
if (gender != GENDER_FEMALE && gender != GENDER_MALE)
throw new IllegalArgumentException("Invalid gender");
this.gender = gender;
}
public byte getHeight() {
return height;
}
public void setHeight(byte height) {
int intHeight = (int)height & 0xff;
if (intHeight < 40 || intHeight > 210)
throw new IllegalArgumentException("Height must be between 40 and 210 cm inclusive");
this.height = height;
}
public byte getWeight() {
return weight;
}
public void setWeight(byte weight) {
int intWeight = (int)weight & 0xff;
if (intWeight < 5 || intWeight > 200)
throw new IllegalArgumentException("Weight must be between 5 and 200 kg inclusive");
this.weight = weight;
}
public byte getAge() {
return age;
}
public void setAge(byte age) {
if (age < 0 || age > 110)
throw new IllegalArgumentException("Age must be between 0 and 110 years inclusive");
this.age = age;
}
public boolean isSetSuccess() {
return setSuccess;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateId(id, LefunConstants.CMD_PROFILE);
if (params.limit() < 1)
throwUnexpectedLength();
op = params.get();
if (op == OP_GET) {
if (params.limit() != 5)
throwUnexpectedLength();
gender = params.get();
height = params.get();
weight = params.get();
age = params.get();
} else if (op == OP_SET) {
if (params.limit() != 2)
throwUnexpectedLength();
setSuccess = params.get() == 1;
} else {
throw new IllegalArgumentException("Invalid operation type received");
}
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(op);
if (op == OP_SET) {
params.put(gender);
params.put(height);
params.put(weight);
params.put(age);
}
return LefunConstants.CMD_PROFILE;
}
}

View File

@ -0,0 +1,36 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class RemoteCameraTriggeredCommand extends BaseCommand {
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateIdAndLength(id, params, LefunConstants.CMD_REMOTE_CAMERA_TRIGGERED, 0);
}
@Override
protected byte serializeParams(ByteBuffer params) {
// No handler on device side
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,46 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class RequestBondingCommand extends BaseCommand {
public static final byte STATUS_ALREADY_BONDED = 0;
public static final byte STATUS_BONDING_SUCCESSFUL = 1;
private byte status;
public byte getStatus() {
return status;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateIdAndLength(id, params, LefunConstants.CMD_BONDING_REQUEST, 1);
status = params.get();
}
@Override
protected byte serializeParams(ByteBuffer params) {
return LefunConstants.CMD_BONDING_REQUEST;
}
}

View File

@ -0,0 +1,86 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class SedentaryReminderIntervalCommand extends BaseCommand {
private byte op;
private byte sedentaryReminderInterval;
private boolean setSuccess;
public byte getOp() {
return op;
}
public void setOp(byte op) {
if (op != OP_GET && op != OP_SET)
throw new IllegalArgumentException("Operation must be get or set");
this.op = op;
}
public byte getSedentaryReminderInterval() {
return sedentaryReminderInterval;
}
public void setSedentaryReminderInterval(byte sedentaryReminderInterval) {
if (sedentaryReminderInterval == 0)
throw new IllegalArgumentException("Interval must be non-zero");
this.sedentaryReminderInterval = sedentaryReminderInterval;
}
public boolean isSetSuccess() {
return setSuccess;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateId(id, LefunConstants.CMD_SEDENTARY_REMINDER_INTERVAL);
if (params.limit() < 1)
throwUnexpectedLength();
op = params.get();
if (op == OP_GET) {
if (params.limit() != 2)
throwUnexpectedLength();
sedentaryReminderInterval = params.get();
} else if (op == OP_SET) {
if (params.limit() != 2)
throwUnexpectedLength();
setSuccess = params.get() == 1;
} else {
throw new IllegalArgumentException("Invalid operation type received");
}
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(op);
if (op == OP_SET) {
params.put(sedentaryReminderInterval);
}
return LefunConstants.CMD_SEDENTARY_REMINDER_INTERVAL;
}
}

View File

@ -0,0 +1,54 @@
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
public class SetLanguageCommand extends BaseCommand {
private byte language;
private boolean setSuccess;
public byte getLanguage() {
return language;
}
public void setLanguage(byte language) {
this.language = language;
}
public boolean isSetSuccess() {
return setSuccess;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateIdAndLength(id, params, LefunConstants.CMD_LANGUAGE, 1);
setSuccess = params.get() == 1;
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(language);
return LefunConstants.CMD_LANGUAGE;
}
}

View File

@ -0,0 +1,54 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class SetRemoteCameraCommand extends BaseCommand {
private boolean remoteCameraEnabled;
private boolean setSuccess;
public boolean getRemoteCameraEnabled() {
return remoteCameraEnabled;
}
public void setRemoteCameraEnabled(boolean remoteCameraEnabled) {
this.remoteCameraEnabled = remoteCameraEnabled;
}
public boolean isSetSuccess() {
return setSuccess;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateIdAndLength(id, params, LefunConstants.CMD_REMOTE_CAMERA, 1);
setSuccess = params.get() == 1;
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put((byte)(remoteCameraEnabled ? 1 : 0));
return LefunConstants.CMD_REMOTE_CAMERA;
}
}

View File

@ -0,0 +1,117 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class SettingsCommand extends BaseCommand {
public static final byte AM_PM_24_HOUR = 0;
public static final byte AM_PM_12_HOUR = 1;
public static final byte MEASUREMENT_UNIT_METRIC = 0;
public static final byte MEASUREMENT_UNIT_IMPERIAL = 1;
private byte op;
private byte option1;
private byte amPmIndicator;
private byte measurementUnit;
private boolean setSuccess;
public byte getOp() {
return op;
}
public void setOp(byte op) {
if (op != OP_GET && op != OP_SET)
throw new IllegalArgumentException("Operation must be get or set");
this.op = op;
}
public byte getOption1() {
return option1;
}
public void setOption1(byte option1) {
if (option1 < 0 || option1 > 24)
throw new IllegalArgumentException("option1 must be between 0 and 24 inclusive");
this.option1 = option1;
}
public byte getAmPmIndicator() {
return amPmIndicator;
}
public void setAmPmIndicator(byte amPmIndicator) {
if (amPmIndicator != AM_PM_12_HOUR && amPmIndicator != AM_PM_24_HOUR)
throw new IllegalArgumentException("Indicator must be 12 or 24 hours");
this.amPmIndicator = amPmIndicator;
}
public byte getMeasurementUnit() {
return measurementUnit;
}
public void setMeasurementUnit(byte measurementUnit) {
if (measurementUnit != MEASUREMENT_UNIT_METRIC && measurementUnit != MEASUREMENT_UNIT_IMPERIAL)
throw new IllegalArgumentException(("Unit must be metric or imperial"));
this.measurementUnit = measurementUnit;
}
public boolean isSetSuccess() {
return setSuccess;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateId(id, LefunConstants.CMD_SETTINGS);
if (params.limit() < 1)
throwUnexpectedLength();
op = params.get();
if (op == OP_GET) {
if (params.limit() != 4)
throwUnexpectedLength();
option1 = params.get();
amPmIndicator = params.get();
measurementUnit = params.get();
} else if (op == OP_SET) {
if (params.limit() != 2)
throwUnexpectedLength();
setSuccess = params.get() == 1;
} else {
throw new IllegalArgumentException("Invalid operation type received");
}
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(op);
if (op == OP_SET) {
params.put(option1);
params.put(amPmIndicator);
params.put(measurementUnit);
}
return LefunConstants.CMD_SETTINGS;
}
}

View File

@ -0,0 +1,57 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class StartPpgSensingCommand extends BaseCommand {
private byte ppgType;
private boolean setSuccess;
public int getPpgType() {
return getLowestSetBitIndex(ppgType);
}
public void setPpgType(int type) {
if (type < 0 || type > 2)
throw new IllegalArgumentException("Invalid PPG type");
this.ppgType = (byte)(1 << type);
}
public boolean isSetSuccess() {
return setSuccess;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateIdAndLength(id, params, LefunConstants.CMD_PPG_START, 2);
ppgType = params.get();
setSuccess = params.get() == 1;
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(ppgType);
return LefunConstants.CMD_PPG_START;
}
}

View File

@ -0,0 +1,149 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class TimeCommand extends BaseCommand {
private byte op;
private byte year;
private byte month;
private byte day;
private byte hour;
private byte minute;
private byte second;
private boolean setSuccess;
public byte getOp() {
if (op != OP_GET && op != OP_SET)
throw new IllegalArgumentException("Operation must be get or set");
return op;
}
public void setOp(byte op) {
this.op = op;
}
public byte getYear() {
return year;
}
public void setYear(byte year) {
this.year = year;
}
public byte getMonth() {
return month;
}
public void setMonth(byte month) {
if (month < 1 || month > 12)
throw new IllegalArgumentException("Month must be between 1 and 12 inclusive");
this.month = month;
}
public byte getDay() {
return day;
}
public void setDay(byte day) {
if (day < 1 || day > 31)
throw new IllegalArgumentException("Day must be between 1 and 31 inclusive");
this.day = day;
}
public byte getHour() {
return hour;
}
public void setHour(byte hour) {
if (hour < 0 || hour > 23)
throw new IllegalArgumentException("Hour must be between 0 and 23 inclusive");
this.hour = hour;
}
public byte getMinute() {
return minute;
}
public void setMinute(byte minute) {
if (minute < 0 || minute > 59)
throw new IllegalArgumentException("Minute must be between 0 and 59 inclusive");
this.minute = minute;
}
public byte getSecond() {
return second;
}
public void setSecond(byte second) {
if (second < 0 || second > 59)
throw new IllegalArgumentException("Second must be between 0 and 59 inclusive");
this.second = second;
}
public boolean isSetSuccess() {
return setSuccess;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateId(id, LefunConstants.CMD_TIME);
if (params.limit() < 1)
throwUnexpectedLength();
op = params.get();
if (op == OP_GET) {
if (params.limit() != 7)
throwUnexpectedLength();
year = params.get();
month = params.get();
day = params.get();
hour = params.get();
minute = params.get();
second = params.get();
} else if (op == OP_SET) {
if (params.limit() != 2)
throwUnexpectedLength();
setSuccess = params.get() == 1;
} else {
throw new IllegalArgumentException("Invalid operation type received");
}
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(op);
if (op == OP_SET) {
params.put(year);
params.put(month);
params.put(day);
params.put(hour);
params.put(minute);
params.put(second);
}
return LefunConstants.CMD_TIME;
}
}

View File

@ -0,0 +1,91 @@
/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Copyright (C) 2020 Yukai Li
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.lefun.commands;
import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.lefun.LefunConstants;
public class UiPagesCommand extends BaseCommand {
// I don't know which pages since they're not implemented in my watch, so no
// constants here for now
private byte op;
private short pages;
private boolean setSuccess;
public byte getOp() {
return op;
}
public void setOp(byte op) {
if (op != OP_GET && op != OP_SET)
throw new IllegalArgumentException("Operation must be get or set");
this.op = op;
}
public boolean getPage(int index) {
if (index < 0 || index >= 16)
throw new IllegalArgumentException("Index must be between 0 and 15 inclusive");
return getBit(pages, 1 << index);
}
public void setPage(int index, boolean enabled) {
if (index < 0 || index >= 16)
throw new IllegalArgumentException("Index must be between 0 and 15 inclusive");
pages = setBit(pages, 1 << index, enabled);
}
public boolean isSetSuccess() {
return setSuccess;
}
@Override
protected void deserializeParams(byte id, ByteBuffer params) {
validateId(id, LefunConstants.CMD_UI_PAGES);
if (params.limit() < 1)
throwUnexpectedLength();
op = params.get();
if (op == OP_GET) {
if (params.limit() != 3)
throwUnexpectedLength();
pages = params.getShort();
} else if (op == OP_SET) {
if (params.limit() != 2)
throwUnexpectedLength();
setSuccess = params.get() == 1;
} else {
throw new IllegalArgumentException("Invalid operation type received");
}
}
@Override
protected byte serializeParams(ByteBuffer params) {
params.put(op);
if (op == OP_SET) {
params.putShort(pages);
}
return LefunConstants.CMD_UI_PAGES;
}
}