mirror of
https://github.com/oliexdev/openScale.git
synced 2025-08-17 22:11:35 +02:00
support for 1byOne model CF398BLE, set clock and download and delete historic measurements (#613)
This commit is contained in:
@@ -25,6 +25,7 @@ import com.health.openscale.core.datatypes.ScaleMeasurement;
|
|||||||
import com.health.openscale.core.datatypes.ScaleUser;
|
import com.health.openscale.core.datatypes.ScaleUser;
|
||||||
import com.health.openscale.core.utils.Converters;
|
import com.health.openscale.core.utils.Converters;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
@@ -36,9 +37,18 @@ public class BluetoothOneByone extends BluetoothCommunication {
|
|||||||
|
|
||||||
private final UUID CMD_MEASUREMENT_CHARACTERISTIC = BluetoothGattUuid.fromShortCode(0xfff1); // write only
|
private final UUID CMD_MEASUREMENT_CHARACTERISTIC = BluetoothGattUuid.fromShortCode(0xfff1); // write only
|
||||||
|
|
||||||
|
private boolean waitAck = false; // if true, resume after receiving acknowledgement
|
||||||
|
private boolean historicMeasurement = false; // processing real-time vs historic measurement
|
||||||
|
private int noHistoric = 0; // number of historic measurements received
|
||||||
|
|
||||||
|
// don't save any measurements closer than 3 seconds to each other
|
||||||
|
private Calendar lastDateTime;
|
||||||
|
private final int DATE_TIME_THRESHOLD = 3000;
|
||||||
|
|
||||||
public BluetoothOneByone(Context context) {
|
public BluetoothOneByone(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
lastDateTime = Calendar.getInstance();
|
||||||
|
lastDateTime.set(2000, 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -72,6 +82,25 @@ public class BluetoothOneByone extends BluetoothCommunication {
|
|||||||
writeBytes(WEIGHT_MEASUREMENT_SERVICE, CMD_MEASUREMENT_CHARACTERISTIC, magicBytes);
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, CMD_MEASUREMENT_CHARACTERISTIC, magicBytes);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
|
Calendar dt = Calendar.getInstance();
|
||||||
|
final byte[] setClockCmd = {(byte)0xf1, (byte)(dt.get(Calendar.YEAR) >> 8),
|
||||||
|
(byte)(dt.get(Calendar.YEAR) & 255), (byte)(dt.get(Calendar.MONTH) + 1),
|
||||||
|
(byte)dt.get(Calendar.DAY_OF_MONTH), (byte)dt.get(Calendar.HOUR_OF_DAY),
|
||||||
|
(byte)dt.get(Calendar.MINUTE), (byte)dt.get(Calendar.SECOND)};
|
||||||
|
waitAck = true;
|
||||||
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, CMD_MEASUREMENT_CHARACTERISTIC, setClockCmd);
|
||||||
|
// 2-byte notification value f1 00 will be received after this command
|
||||||
|
stopMachineState(); // we will resume after receiving acknowledgement f1 00
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
// request historic measurements; they are followed by real-time measurements
|
||||||
|
historicMeasurement = true;
|
||||||
|
final byte[] getHistoryCmd = {(byte)0xf2, (byte)0x00};
|
||||||
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, CMD_MEASUREMENT_CHARACTERISTIC, getHistoryCmd);
|
||||||
|
// multiple measurements will be received, they start cf ... and are 11 or 18 bytes long
|
||||||
|
// 2-byte notification value f2 00 follows last historic measurement
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
sendMessage(R.string.info_step_on_scale, 0);
|
sendMessage(R.string.info_step_on_scale, 0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -89,8 +118,28 @@ public class BluetoothOneByone extends BluetoothCommunication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if data is valid data
|
// if data is valid data
|
||||||
if (data.length == 20 && data[0] == (byte)0xcf) {
|
if (data.length >= 11 && data[0] == (byte)0xcf) {
|
||||||
|
if (historicMeasurement) {
|
||||||
|
++noHistoric;
|
||||||
|
}
|
||||||
parseBytes(data);
|
parseBytes(data);
|
||||||
|
} else {
|
||||||
|
// show 2-byte ack messages in debug output:
|
||||||
|
// f1 00 setClockCmd acknowledgement
|
||||||
|
// f2 00 end of historic measurements, real-time measurements follow
|
||||||
|
// f2 01 clearHistoryCmd acknowledgement
|
||||||
|
Timber.d("received bytes [%s]", byteInHex(data));
|
||||||
|
|
||||||
|
if (waitAck && data.length == 2 && data[0] == (byte)0xf1 && data[1] == 0) {
|
||||||
|
waitAck = false;
|
||||||
|
resumeMachineState();
|
||||||
|
} else if (data.length == 2 && data[0] == (byte)0xf2 && data[1] == 0) {
|
||||||
|
historicMeasurement = false;
|
||||||
|
if (noHistoric > 0) {
|
||||||
|
final byte[] clearHistoryCmd = {(byte)0xf2, (byte)0x01};
|
||||||
|
writeBytes(WEIGHT_MEASUREMENT_SERVICE, CMD_MEASUREMENT_CHARACTERISTIC, clearHistoryCmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,6 +147,21 @@ public class BluetoothOneByone extends BluetoothCommunication {
|
|||||||
float weight = Converters.fromUnsignedInt16Le(weightBytes, 3) / 100.0f;
|
float weight = Converters.fromUnsignedInt16Le(weightBytes, 3) / 100.0f;
|
||||||
int impedanceCoeff = Converters.fromUnsignedInt24Le(weightBytes, 5);
|
int impedanceCoeff = Converters.fromUnsignedInt24Le(weightBytes, 5);
|
||||||
int impedanceValue = weightBytes[5] + weightBytes[6] + weightBytes[7];
|
int impedanceValue = weightBytes[5] + weightBytes[6] + weightBytes[7];
|
||||||
|
boolean impedancePresent = (weightBytes[9] != 1) && (impedanceCoeff != 0);
|
||||||
|
boolean dateTimePresent = weightBytes.length >= 18;
|
||||||
|
|
||||||
|
if (!impedancePresent || (!dateTimePresent && historicMeasurement)) {
|
||||||
|
// unwanted, no impedance or historic measurement w/o time-stamp
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Calendar dateTime = Calendar.getInstance();
|
||||||
|
if (dateTimePresent) {
|
||||||
|
// 18-byte or longer measurements contain date and time, used in history
|
||||||
|
dateTime.set(Converters.fromUnsignedInt16Be(weightBytes, 11),
|
||||||
|
weightBytes[13] - 1, weightBytes[14], weightBytes[15],
|
||||||
|
weightBytes[16], weightBytes[17]);
|
||||||
|
}
|
||||||
|
|
||||||
final ScaleUser scaleUser = OpenScale.getInstance().getSelectedScaleUser();
|
final ScaleUser scaleUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||||
|
|
||||||
@@ -135,6 +199,19 @@ public class BluetoothOneByone extends BluetoothCommunication {
|
|||||||
|
|
||||||
ScaleMeasurement scaleBtData = new ScaleMeasurement();
|
ScaleMeasurement scaleBtData = new ScaleMeasurement();
|
||||||
scaleBtData.setWeight(weight);
|
scaleBtData.setWeight(weight);
|
||||||
|
try {
|
||||||
|
dateTime.setLenient(false);
|
||||||
|
scaleBtData.setDateTime(dateTime.getTime());
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
if (historicMeasurement) {
|
||||||
|
Timber.d("invalid time-stamp: year %d, month %d, day %d, hour %d, minute %d, second %d",
|
||||||
|
Converters.fromUnsignedInt16Be(weightBytes, 11),
|
||||||
|
weightBytes[13], weightBytes[14], weightBytes[15],
|
||||||
|
weightBytes[16], weightBytes[17]);
|
||||||
|
return; // discard historic measurement with invalid time-stamp
|
||||||
|
}
|
||||||
|
}
|
||||||
scaleBtData.setFat(oneByoneLib.getBodyFat(weight, impedanceCoeff));
|
scaleBtData.setFat(oneByoneLib.getBodyFat(weight, impedanceCoeff));
|
||||||
scaleBtData.setWater(oneByoneLib.getWater(scaleBtData.getFat()));
|
scaleBtData.setWater(oneByoneLib.getWater(scaleBtData.getFat()));
|
||||||
scaleBtData.setBone(oneByoneLib.getBoneMass(weight, impedanceValue));
|
scaleBtData.setBone(oneByoneLib.getBoneMass(weight, impedanceValue));
|
||||||
@@ -143,6 +220,11 @@ public class BluetoothOneByone extends BluetoothCommunication {
|
|||||||
|
|
||||||
Timber.d("scale measurement [%s]", scaleBtData);
|
Timber.d("scale measurement [%s]", scaleBtData);
|
||||||
|
|
||||||
|
if (dateTime.getTimeInMillis() - lastDateTime.getTimeInMillis() < DATE_TIME_THRESHOLD) {
|
||||||
|
return; // don't save measurements too close to each other
|
||||||
|
}
|
||||||
|
lastDateTime = dateTime;
|
||||||
|
|
||||||
addScaleMeasurement(scaleBtData);
|
addScaleMeasurement(scaleBtData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user