mirror of
https://github.com/oliexdev/openScale.git
synced 2025-08-12 11:44:19 +02:00
added reversed engineered mi scale v2 library to support body measurements
This commit is contained in:
@@ -21,16 +21,10 @@ import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import com.health.openscale.core.OpenScale;
|
||||
import com.health.openscale.core.bodymetric.EstimatedFatMetric;
|
||||
import com.health.openscale.core.bodymetric.EstimatedLBMMetric;
|
||||
import com.health.openscale.core.bodymetric.EstimatedWaterMetric;
|
||||
import com.health.openscale.core.bluetooth.lib.MiScaleLib;
|
||||
import com.health.openscale.core.datatypes.ScaleMeasurement;
|
||||
import com.health.openscale.core.datatypes.ScaleUser;
|
||||
import com.health.openscale.core.utils.Converters;
|
||||
import com.health.openscale.gui.views.FatMeasurementView;
|
||||
import com.health.openscale.gui.views.LBMMeasurementView;
|
||||
import com.health.openscale.gui.views.MeasurementViewSettings;
|
||||
import com.health.openscale.gui.views.WaterMeasurementView;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
@@ -143,31 +137,39 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void parseBytes(byte[] weightBytes) {
|
||||
private void parseBytes(byte[] data) {
|
||||
try {
|
||||
final byte ctrlByte0 = weightBytes[0];
|
||||
final byte ctrlByte1 = weightBytes[1];
|
||||
final byte ctrlByte0 = data[0];
|
||||
final byte ctrlByte1 = data[1];
|
||||
|
||||
final boolean isWeightRemoved = isBitSet(ctrlByte1, 7);
|
||||
final boolean isDateInvalid = isBitSet(ctrlByte1, 6);
|
||||
final boolean isStabilized = isBitSet(ctrlByte1, 5);
|
||||
final boolean isLBSUnit = isBitSet(ctrlByte0, 0);
|
||||
final boolean isCattyUnit = isBitSet(ctrlByte1, 6);
|
||||
final boolean isImpedance = isBitSet(ctrlByte1, 1);
|
||||
|
||||
if (isStabilized && !isWeightRemoved && !isDateInvalid) {
|
||||
|
||||
final int year = ((weightBytes[3] & 0xFF) << 8) | (weightBytes[2] & 0xFF);
|
||||
final int month = (int) weightBytes[4];
|
||||
final int day = (int) weightBytes[5];
|
||||
final int hours = (int) weightBytes[6];
|
||||
final int min = (int) weightBytes[7];
|
||||
final int sec = (int) weightBytes[8];
|
||||
final int year = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
|
||||
final int month = (int) data[4];
|
||||
final int day = (int) data[5];
|
||||
final int hours = (int) data[6];
|
||||
final int min = (int) data[7];
|
||||
final int sec = (int) data[8];
|
||||
|
||||
float weight;
|
||||
float impedance = 0.0f;
|
||||
|
||||
if (isLBSUnit || isCattyUnit) {
|
||||
weight = (float) (((weightBytes[12] & 0xFF) << 8) | (weightBytes[11] & 0xFF)) / 100.0f;
|
||||
weight = (float) (((data[12] & 0xFF) << 8) | (data[11] & 0xFF)) / 100.0f;
|
||||
} else {
|
||||
weight = (float) (((weightBytes[12] & 0xFF) << 8) | (weightBytes[11] & 0xFF)) / 200.0f;
|
||||
weight = (float) (((data[12] & 0xFF) << 8) | (data[11] & 0xFF)) / 200.0f;
|
||||
}
|
||||
|
||||
if (isImpedance) {
|
||||
impedance = ((data[10] & 0xFF) << 8) | (data[9] & 0xFF);
|
||||
Timber.d("impedance value is " + impedance);
|
||||
}
|
||||
|
||||
String date_string = year + "/" + month + "/" + day + "/" + hours + "/" + min;
|
||||
@@ -175,29 +177,31 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
||||
|
||||
// Is the year plausible? Check if the year is in the range of 20 years...
|
||||
if (validateDate(date_time, 20)) {
|
||||
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||
final ScaleUser scaleUser = OpenScale.getInstance().getSelectedScaleUser();
|
||||
ScaleMeasurement scaleBtData = new ScaleMeasurement();
|
||||
|
||||
scaleBtData.setWeight(Converters.toKilogram(weight, selectedUser.getScaleUnit()));
|
||||
scaleBtData.setWeight(Converters.toKilogram(weight, scaleUser.getScaleUnit()));
|
||||
scaleBtData.setDateTime(date_time);
|
||||
|
||||
// estimate fat, water and LBM until library is reversed engineered
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
int sex;
|
||||
|
||||
MeasurementViewSettings settings = new MeasurementViewSettings(prefs, WaterMeasurementView.KEY);
|
||||
EstimatedWaterMetric waterMetric = EstimatedWaterMetric.getEstimatedMetric(
|
||||
EstimatedWaterMetric.FORMULA.valueOf(settings.getEstimationFormula()));
|
||||
scaleBtData.setWater(waterMetric.getWater(selectedUser, scaleBtData));
|
||||
if (scaleUser.getGender() == Converters.Gender.MALE) {
|
||||
sex = 1;
|
||||
} else {
|
||||
sex = 0;
|
||||
}
|
||||
|
||||
settings = new MeasurementViewSettings(prefs, FatMeasurementView.KEY);
|
||||
EstimatedFatMetric fatMetric = EstimatedFatMetric.getEstimatedMetric(
|
||||
EstimatedFatMetric.FORMULA.valueOf(settings.getEstimationFormula()));
|
||||
scaleBtData.setFat(fatMetric.getFat(selectedUser, scaleBtData));
|
||||
if (impedance != 0.0f) {
|
||||
MiScaleLib miScaleLib = new MiScaleLib(sex, scaleUser.getAge(), scaleUser.getBodyHeight());
|
||||
|
||||
settings = new MeasurementViewSettings(prefs, LBMMeasurementView.KEY);
|
||||
EstimatedLBMMetric lbmMetric = EstimatedLBMMetric.getEstimatedMetric(
|
||||
EstimatedLBMMetric.FORMULA.valueOf(settings.getEstimationFormula()));
|
||||
scaleBtData.setLbm(lbmMetric.getLBM(selectedUser, scaleBtData));
|
||||
scaleBtData.setWater(miScaleLib.getWater(weight, impedance));
|
||||
scaleBtData.setVisceralFat(miScaleLib.getVisceralFat(weight));
|
||||
scaleBtData.setFat(miScaleLib.getBodyFat(weight, impedance));
|
||||
scaleBtData.setMuscle(miScaleLib.getMuscle(weight, impedance));
|
||||
scaleBtData.setBone(miScaleLib.getBoneMass(weight, impedance));
|
||||
} else {
|
||||
Timber.d("Impedance value is zero");
|
||||
}
|
||||
|
||||
addScaleMeasurement(scaleBtData);
|
||||
} else {
|
||||
|
@@ -0,0 +1,164 @@
|
||||
/* Copyright (C) 2019 olie.xdev <olie.xdev@googlemail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* based on https://github.com/prototux/MIBCS-reverse-engineering by prototux
|
||||
*/
|
||||
|
||||
package com.health.openscale.core.bluetooth.lib;
|
||||
|
||||
public class MiScaleLib {
|
||||
private int sex; // male = 1; female = 0
|
||||
private int age;
|
||||
private float height;
|
||||
|
||||
public MiScaleLib(int sex, int age, float height) {
|
||||
this.sex = sex;
|
||||
this.age = age;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
private float getLBMCoefficient(float weight, float impedance) {
|
||||
float lbm = (height * 9.058f / 100.0f) * (height / 100.0f);
|
||||
lbm += weight * 0.32f + 12.226f;
|
||||
lbm -= impedance * 0.0068f;
|
||||
lbm -= age * 0.0542f;
|
||||
|
||||
return lbm;
|
||||
}
|
||||
|
||||
public float getBMI(float weight) {
|
||||
return weight / (((height * height) / 100.0f) / 100.0f);
|
||||
}
|
||||
|
||||
public float getMuscle(float weight, float impedance) {
|
||||
float muscleMass = weight - ((getBodyFat(weight, impedance) * 0.01f) * weight) - getBoneMass(weight, impedance);
|
||||
|
||||
if (sex == 0 && muscleMass >= 84.0f) {
|
||||
muscleMass = 120.0f;
|
||||
}
|
||||
else if (sex == 1 && muscleMass >= 93.5f) {
|
||||
muscleMass = 120.0f;
|
||||
}
|
||||
|
||||
return muscleMass;
|
||||
}
|
||||
|
||||
public float getWater(float weight, float impedance) {
|
||||
float coeff;
|
||||
float water = (100.0f - getBodyFat(weight, impedance)) * 0.7f;
|
||||
|
||||
if (water < 50) {
|
||||
coeff = 1.02f;
|
||||
} else {
|
||||
coeff = 0.98f;
|
||||
}
|
||||
|
||||
return coeff * water;
|
||||
}
|
||||
|
||||
public float getBoneMass(float weight, float impedance) {
|
||||
float boneMass;
|
||||
float base;
|
||||
|
||||
if (sex == 0) {
|
||||
base = 0.245691014f;
|
||||
}
|
||||
else {
|
||||
base = 0.18016894f;
|
||||
}
|
||||
|
||||
boneMass = (base - (getLBMCoefficient(weight, impedance) * 0.05158f)) * -1.0f;
|
||||
|
||||
if (boneMass > 2.2f) {
|
||||
boneMass += 0.1f;
|
||||
}
|
||||
else {
|
||||
boneMass -= 0.1f;
|
||||
}
|
||||
|
||||
if (sex == 0 && boneMass > 5.1f) {
|
||||
boneMass = 8.0f;
|
||||
}
|
||||
else if (sex == 1 && boneMass > 5.2f) {
|
||||
boneMass = 8.0f;
|
||||
}
|
||||
|
||||
return boneMass;
|
||||
}
|
||||
|
||||
public float getVisceralFat(float weight) {
|
||||
float visceralFat = 0.0f;
|
||||
if (sex == 0) {
|
||||
if (weight > (13.0f - (height * 0.5f)) * -1.0f) {
|
||||
float subsubcalc = ((height * 1.45f) + (height * 0.1158f) * height) - 120.0f;
|
||||
float subcalc = weight * 500.0f / subsubcalc;
|
||||
visceralFat = (subcalc - 6.0f) + (age * 0.07f);
|
||||
}
|
||||
else {
|
||||
float subcalc = 0.691f + (height * -0.0024f) + (height * -0.0024f);
|
||||
visceralFat = (((height * 0.027f) - (subcalc * weight)) * -1.0f) + (age * 0.07f) - age;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (height < weight * 1.6f) {
|
||||
float subcalc = ((height * 0.4f) - (height * (height * 0.0826f))) * -1.0f;
|
||||
visceralFat = ((weight * 305.0f) / (subcalc + 48.0f)) - 2.9f + (age * 0.15f);
|
||||
}
|
||||
else {
|
||||
float subcalc = 0.765f + height * -0.0015f;
|
||||
visceralFat = (((height * 0.143f) - (weight * subcalc)) * -1.0f) + (age * 0.15f) - 5.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return visceralFat;
|
||||
}
|
||||
|
||||
public float getBodyFat(float weight, float impedance) {
|
||||
float bodyFat = 0.0f;
|
||||
float lbmSub = 0.8f;
|
||||
|
||||
if (sex == 0 && age <= 49) {
|
||||
lbmSub = 9.25f;
|
||||
} else if (sex == 1 && age > 49) {
|
||||
lbmSub = 7.25f;
|
||||
}
|
||||
|
||||
float lbmCoeff = getLBMCoefficient(weight, impedance);
|
||||
|
||||
if (sex == 1 && weight < 61) {
|
||||
bodyFat = (1.0f - (((lbmCoeff - lbmSub) * 0.98f) / weight)) * 100.0f;
|
||||
}
|
||||
else if (sex == 0 && weight < 50) {
|
||||
bodyFat = (1.0f - (((lbmCoeff - lbmSub) * 1.02f) / weight)) * 100.0f;
|
||||
}
|
||||
else if (sex == 0 && weight < 60) {
|
||||
bodyFat = (1.0f - (((lbmCoeff - lbmSub) * 0.96f) / weight)) * 100.0f;
|
||||
}
|
||||
else if (sex == 0 && height < 160) {
|
||||
bodyFat = (1.0f - (((lbmCoeff - lbmSub) * 1.03f) / weight)) * 100.0f;
|
||||
} else {
|
||||
bodyFat = (1.0f - ((lbmCoeff - lbmSub) / weight)) * 100.0f;
|
||||
}
|
||||
|
||||
if (bodyFat > 63.0f) {
|
||||
bodyFat = 75.0f;
|
||||
}
|
||||
|
||||
return bodyFat;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user