1
0
mirror of https://github.com/oliexdev/openScale.git synced 2025-08-06 16:56:55 +02:00

Senssun scale (#437)

* refactored Senssun scale
* added Fitindex ES-26M scale to list of supported scales
This commit is contained in:
OliE
2019-03-17 08:46:21 +01:00
committed by GitHub
parent e1f787fef5
commit 2edc6bb707
2 changed files with 175 additions and 87 deletions

View File

@@ -16,26 +16,36 @@
package com.health.openscale.core.bluetooth;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import com.health.openscale.R;
import com.health.openscale.core.OpenScale;
import com.health.openscale.core.datatypes.ScaleMeasurement;
import com.health.openscale.core.datatypes.ScaleUser;
import com.health.openscale.core.utils.Converters;
import com.polidea.rxandroidble2.RxBleDeviceServices;
import java.util.Calendar;
import java.util.Date;
import java.util.UUID;
import timber.log.Timber;
public class BluetoothSenssun extends BluetoothCommunication {
private final UUID WEIGHT_MEASUREMENT_SERVICE = BluetoothGattUuid.fromShortCode(0xfff0);
private final UUID WEIGHT_MEASUREMENT_CHARACTERISTIC = BluetoothGattUuid.fromShortCode(0xfff1); // read, notify
private final UUID CMD_MEASUREMENT_CHARACTERISTIC = BluetoothGattUuid.fromShortCode(0xfff2); // write only
private final UUID MODEL_A_MEASUREMENT_SERVICE = UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb");
private final UUID MODEL_A_NOTIFICATION_CHARACTERISTIC = UUID.fromString("0000fff1-0000-1000-8000-00805f9b34fb");
private final UUID MODEL_A_WRITE_CHARACTERISTIC = UUID.fromString("0000fff2-0000-1000-8000-00805f9b34fb");
private boolean scaleGotUserData;
private byte WeightFatMus = 0;
private ScaleMeasurement measurement;
private final UUID MODEL_B_MEASUREMENT_SERVICE = UUID.fromString("0000ffb0-0000-1000-8000-00805f9b34fb");
private final UUID MODEL_B_NOTIFICATION_CHARACTERISTIC = UUID.fromString("0000ffb2-0000-1000-8000-00805f9b34fb");
private final UUID MODEL_B_WRITE_CHARACTERISTIC = UUID.fromString("0000ffb2-0000-1000-8000-00805f9b34fb");
private UUID writeCharacteristic;
private int lastWeight, lastFat, lastHydration, lastMuscle, lastBone, lastKcal;
private boolean weightStabilized, stepMessageDisplayed;
private int values;
public BluetoothSenssun(Context context) {
super(context);
@@ -43,114 +53,192 @@ public class BluetoothSenssun extends BluetoothCommunication {
@Override
public String driverName() {
return "Senssun";
return "Senssun Fat";
}
private void sendUserData(){
if ( scaleGotUserData ){
return;
@Override
protected void onBluetoothDiscovery(RxBleDeviceServices rxBleDeviceServices) {
for (BluetoothGattService gattService : rxBleDeviceServices.getBluetoothGattServices()) {
if (gattService.getUuid().equals(MODEL_A_MEASUREMENT_SERVICE)) {
writeCharacteristic = MODEL_A_WRITE_CHARACTERISTIC;
setNotificationOn(MODEL_A_NOTIFICATION_CHARACTERISTIC);
Timber.d("Found a Model A");
break;
}
if (gattService.getUuid().equals(MODEL_B_MEASUREMENT_SERVICE)) {
writeCharacteristic = MODEL_B_WRITE_CHARACTERISTIC;
setNotificationOn(MODEL_B_NOTIFICATION_CHARACTERISTIC);
Timber.d("Found a Model B");
break;
}
}
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
byte gender = selectedUser.getGender().isMale() ? (byte)0xf1 : (byte)0x01;
byte height = (byte) selectedUser.getBodyHeight(); // cm
byte age = (byte) selectedUser.getAge();
Timber.d("Request Saved User Measurements ");
byte cmdByte[] = {(byte)0xa5, (byte)0x10, gender, age, height, (byte)0, (byte)0x0, (byte)0x0d2, (byte)0x00};
byte verify = 0;
for (int i = 1; i < cmdByte.length - 2; i++) {
verify = (byte) (verify + cmdByte[i]);
}
cmdByte[cmdByte.length - 2] = verify;
writeBytes(CMD_MEASUREMENT_CHARACTERISTIC, cmdByte);
resumeMachineState();
}
@Override
protected boolean onNextStep(int stepNr) {
switch (stepNr) {
case 0:
setNotificationOn(WEIGHT_MEASUREMENT_CHARACTERISTIC);
weightStabilized = false;
stepMessageDisplayed = false;
values = 0;
discoverBluetoothServices();
stopMachineState();
break;
case 1:
sendUserData();
WeightFatMus = 0;
scaleGotUserData = false;
Timber.d("Sync Date");
synchroniseDate();
break;
case 2:
Timber.d("Sync Time");
synchroniseTime();
break;
default:
// Finish init if everything is done
return false;
}
return true;
}
@Override
public void onBluetoothNotify(UUID characteristic, byte[] value) {
final byte[] data = value;
// The first notification only includes weight and all other fields are
// either 0x00 (user info) or 0xff (fat, water, etc.)
if (data != null && !isBitSet(WeightFatMus, 3)) { //only if not saved
parseBytes(data);
if (value == null || value[0] != (byte)0xFF) {
return;
}
if (isBitSet(WeightFatMus,2) ) {
addScaleMeasurement(measurement);
System.arraycopy(value, 1, value, 0, value.length - 1);
switch (value[0]) {
case (byte)0xA5:
parseMeasurement(value);
break;
}
}
private void parseMeasurement(byte[] data) {
switch(data[5]) {
case (byte)0xAA:
case (byte)0xA0:
if (weightStabilized) {
return;
}
if (!stepMessageDisplayed) {
sendMessage(R.string.info_step_on_scale, 0);
stepMessageDisplayed = true;
}
weightStabilized = data[5] == (byte)0xAA;
Timber.d("the byte is %d stable is %s", (data[5] & 0xff), weightStabilized ? "true": "false");
lastWeight = ((data[1] & 0xff) << 8) | (data[2] & 0xff);
if (lastWeight > 0) {
sendMessage(R.string.info_measuring, lastWeight / 10.0f);
}
if (weightStabilized) {
values |= 1;
synchroniseUser();
}
break;
case (byte)0xBE:
setBluetoothStatus(BT_STATUS.UNEXPECTED_ERROR, "Fat Test Error");
disconnect();
break;
case (byte)0xB0:
lastFat = ((data[1] & 0xff) << 8) | (data[2] & 0xff);
lastHydration = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
values |= 2;
Timber.d("got fat %d", values);
break;
case (byte)0xC0:
lastMuscle = ((data[1] & 0xff) << 8) | (data[2] & 0xff);
lastBone = ((data[4] & 0xff) << 8) | (data[3] & 0xff);
values |= 4;
Timber.d("got muscle %d", values);
break;
case (byte)0xD0:
lastKcal = ((data[1] & 0xff) << 8) | (data[2] & 0xff);
int unknown = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
values |= 8;
Timber.d("got kal %d", values);
break;
}
if (values == 15) {
ScaleMeasurement scaleBtData = new ScaleMeasurement();
scaleBtData.setWeight((float)lastWeight / 10.0f);
scaleBtData.setFat((float)lastFat / 10.0f);
scaleBtData.setWater((float)lastHydration / 10.0f);
scaleBtData.setBone((float)lastBone / 10.0f);
scaleBtData.setMuscle((float)lastMuscle / 10.0f);
scaleBtData.setDateTime(new Date());
addScaleMeasurement(scaleBtData);
disconnect();
}
}
private void parseBytes(byte[] weightBytes) {
if (measurement == null) {
measurement = new ScaleMeasurement();
}
int type = weightBytes[6] & 0xff;
Timber.d("type %02X", type);
switch (type) {
case 0x00:
if (weightBytes[2] == (byte)0x10) {
scaleGotUserData = true;
}
break;
case 0xa0:
sendUserData();
break;
case 0xaa:
float weight = Converters.fromUnsignedInt16Be(weightBytes, 2) / 10.0f; // kg
measurement.setWeight(weight);
private void synchroniseDate() {
Calendar cal = Calendar.getInstance();
if (!isBitSet(WeightFatMus,2)){
WeightFatMus |= 1 << 2 ;
}
byte message[] = new byte[]{(byte)0xA5, (byte)0x30, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00};
message[2] = (byte)Integer.parseInt(Long.toHexString(Integer.valueOf(String.valueOf(cal.get(Calendar.YEAR)).substring(2))), 16);
sendUserData();
break;
case 0xb0:
float fat = Converters.fromUnsignedInt16Be(weightBytes, 2) / 10.0f; // %
float water = Converters.fromUnsignedInt16Be(weightBytes, 4) / 10.0f; // %
measurement.setFat(fat);
measurement.setWater(water);
WeightFatMus |= 1 << 1;
break;
case 0xc0:
float bone = Converters.fromUnsignedInt16Le(weightBytes, 4) / 10.0f; // kg
float muscle = Converters.fromUnsignedInt16Be(weightBytes, 2) / 10.0f; // %
measurement.setMuscle(muscle);
measurement.setBone(bone);
WeightFatMus |= 1;
break;
case 0xd0:
float calorie = Converters.fromUnsignedInt16Be(weightBytes, 2);
break;
case 0xe0:
break;
case 0xe1:
break;
case 0xe2:
//date
break;
String DayLength=Long.toHexString(cal.get(Calendar.DAY_OF_YEAR));
DayLength=DayLength.length()==1?"000"+DayLength:
DayLength.length()==2?"00"+DayLength:
DayLength.length()==3?"0"+DayLength:DayLength;
message[3]=(byte)Integer.parseInt(DayLength.substring(0,2), 16);
message[4]=(byte)Integer.parseInt(DayLength.substring(2,4), 16);
addChecksum(message);
writeBytes(writeCharacteristic, message);
}
private void synchroniseTime() {
Calendar cal = Calendar.getInstance();
byte message[] = new byte[]{(byte)0xA5, (byte)0x31, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00};
message[2]=(byte)Integer.parseInt(Long.toHexString(cal.get(Calendar.HOUR_OF_DAY)), 16);
message[3]=(byte)Integer.parseInt(Long.toHexString(cal.get(Calendar.MINUTE)), 16);
message[4]=(byte)Integer.parseInt(Long.toHexString(cal.get(Calendar.SECOND)), 16);
addChecksum(message);
writeBytes(writeCharacteristic, message);
}
private void addChecksum(byte[] message) {
byte verify = 0;
for(int i=1;i<message.length-2;i++){
verify=(byte) (verify+message[i] & 0xff);
}
measurement.setDateTime(new Date());
message[message.length-2]=verify;
}
private void synchroniseUser() {
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
byte message[] = new byte[]{(byte)0xA5, (byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00};
//message[2] = (byte)((selectedUser.getGender().isMale() ? (byte)0x80: (byte)0x00) + 1+selectedUser.getId());
message[2] = (byte) ((byte)(selectedUser.getGender().isMale()?(byte)0:(byte)8)*(byte)16 + (byte)selectedUser.getId());
message[3] = (byte)selectedUser.getAge();
message[4] = (byte)selectedUser.getBodyHeight();
addChecksum(message);
writeBytes(writeCharacteristic, message);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB