mirror of
https://github.com/oliexdev/openScale.git
synced 2025-08-23 08:43:15 +02:00
Refactor unique number generation for legacy scales. Additionally, fix corrected date parsing and history data for the MiScale2 driver:
This commit is contained in:
@@ -83,6 +83,8 @@ public abstract class BluetoothCommunication {
|
|||||||
private List<ScaleUser> cachedAppUserList;
|
private List<ScaleUser> cachedAppUserList;
|
||||||
protected ScaleMeasurement cachedLastMeasurementForSelectedUser;
|
protected ScaleMeasurement cachedLastMeasurementForSelectedUser;
|
||||||
|
|
||||||
|
private int uniqueBase = -1;
|
||||||
|
|
||||||
public BluetoothCommunication(Context context)
|
public BluetoothCommunication(Context context)
|
||||||
{
|
{
|
||||||
this.context = context;
|
this.context = context;
|
||||||
@@ -129,6 +131,21 @@ public abstract class BluetoothCommunication {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUniqueNumber(int base) {
|
||||||
|
this.uniqueBase = base;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getUniqueNumber() {
|
||||||
|
if (uniqueBase == -1) {
|
||||||
|
LogManager.w(TAG, "(Unique number base not set! Call setUniqueNumber() first.", null);
|
||||||
|
uniqueBase = 99;
|
||||||
|
}
|
||||||
|
int userId = getSelectedScaleUser().getId();
|
||||||
|
int finalUnique = uniqueBase + userId;
|
||||||
|
LogManager.d(TAG, "Returning unique number " + finalUnique + " (base=" + uniqueBase + " + userId=" + userId + ")");
|
||||||
|
return finalUnique;
|
||||||
|
}
|
||||||
|
|
||||||
protected void requestUserInteraction(UserInteractionType interactionType, Object data) {
|
protected void requestUserInteraction(UserInteractionType interactionType, Object data) {
|
||||||
if (callbackBtHandler != null) {
|
if (callbackBtHandler != null) {
|
||||||
Message msg = callbackBtHandler.obtainMessage(BT_STATUS.USER_INTERACTION_REQUIRED.ordinal());
|
Message msg = callbackBtHandler.obtainMessage(BT_STATUS.USER_INTERACTION_REQUIRED.ordinal());
|
||||||
|
@@ -223,23 +223,4 @@ public class BluetoothMiScale extends BluetoothCommunication {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getUniqueNumber() {
|
|
||||||
int uniqueNumber;
|
|
||||||
|
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
|
|
||||||
uniqueNumber = prefs.getInt("uniqueNumber", 0x00);
|
|
||||||
|
|
||||||
if (uniqueNumber == 0x00) {
|
|
||||||
Random r = new Random();
|
|
||||||
uniqueNumber = r.nextInt(65535 - 100 + 1) + 100;
|
|
||||||
|
|
||||||
prefs.edit().putInt("uniqueNumber", uniqueNumber).apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
int userId = getSelectedScaleUser().getId();
|
|
||||||
|
|
||||||
return uniqueNumber + userId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -49,6 +49,8 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
|||||||
private int pendingHistoryCount = -1;
|
private int pendingHistoryCount = -1;
|
||||||
private int importedHistory = 0;
|
private int importedHistory = 0;
|
||||||
|
|
||||||
|
private boolean historyMode = false;
|
||||||
|
|
||||||
// warn only once per session if we see "unexpected" flags in history status byte
|
// warn only once per session if we see "unexpected" flags in history status byte
|
||||||
private boolean historyUnexpectedFlagWarned = false;
|
private boolean historyUnexpectedFlagWarned = false;
|
||||||
|
|
||||||
@@ -65,6 +67,8 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
|||||||
|
|
||||||
// STOP (end of history transfer)
|
// STOP (end of history transfer)
|
||||||
if (value.length == 1 && value[0] == 0x03) {
|
if (value.length == 1 && value[0] == 0x03) {
|
||||||
|
historyMode = false;
|
||||||
|
|
||||||
// Check for truncated leftover BEFORE flush (for debug visibility)
|
// Check for truncated leftover BEFORE flush (for debug visibility)
|
||||||
int leftoverLen = histBuf.size();
|
int leftoverLen = histBuf.size();
|
||||||
if (leftoverLen % 10 != 0) {
|
if (leftoverLen % 10 != 0) {
|
||||||
@@ -95,15 +99,27 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
|||||||
|
|
||||||
// Live frame (13 bytes)
|
// Live frame (13 bytes)
|
||||||
if (value.length == 13) {
|
if (value.length == 13) {
|
||||||
|
if (historyMode) {
|
||||||
|
if (parseLiveFrame(value)) {
|
||||||
|
importedHistory++;
|
||||||
|
LogManager.d(TAG, "History (13B) parsed");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
parseLiveFrame(value);
|
parseLiveFrame(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Rare: two live frames combined in one notify (2x13)
|
// Rare: two live frames combined in one notify (2x13)
|
||||||
if (value.length == 26) {
|
if (value.length == 26) {
|
||||||
LogManager.d(TAG, "26-byte payload -> split into 2x13 live frames");
|
LogManager.d(TAG, "26-byte payload -> split into 2x13 live frames");
|
||||||
parseLiveFrame(Arrays.copyOfRange(value, 0, 13));
|
boolean s1 = parseLiveFrame(Arrays.copyOfRange(value, 0, 13));
|
||||||
parseLiveFrame(Arrays.copyOfRange(value, 13, 26));
|
boolean s2 = parseLiveFrame(Arrays.copyOfRange(value, 13, 26));
|
||||||
|
if (historyMode) {
|
||||||
|
int inc = (s1 ? 1 : 0) + (s2 ? 1 : 0);
|
||||||
|
if (inc > 0) importedHistory += inc;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,6 +176,7 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
|||||||
case 4: { // trigger history transfer
|
case 4: { // trigger history transfer
|
||||||
writeBytes(BluetoothGattUuid.SERVICE_BODY_COMPOSITION, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x02});
|
writeBytes(BluetoothGattUuid.SERVICE_BODY_COMPOSITION, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x02});
|
||||||
LogManager.d(TAG, "History transfer triggered (0x02)");
|
LogManager.d(TAG, "History transfer triggered (0x02)");
|
||||||
|
historyMode = true;
|
||||||
stopMachineState(); // wait for notifications
|
stopMachineState(); // wait for notifications
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -171,27 +188,22 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
|||||||
|
|
||||||
// --- Parsing ---
|
// --- Parsing ---
|
||||||
|
|
||||||
private void parseLiveFrame(byte[] data) {
|
private boolean parseLiveFrame(byte[] data) {
|
||||||
// ctrl bytes
|
final byte c0 = data[0], c1 = data[1];
|
||||||
final byte c0 = data[0];
|
final boolean isLbs = isBitSet(c0,0), isCatty = isBitSet(c1,6),
|
||||||
final byte c1 = data[1];
|
isStabilized = isBitSet(c1,5), isRemoved = isBitSet(c1,7),
|
||||||
|
isImpedance = isBitSet(c1,1);
|
||||||
final boolean isLbs = isBitSet(c0, 0);
|
|
||||||
final boolean isCatty = isBitSet(c1, 6); // catty/jin
|
|
||||||
final boolean isStabilized = isBitSet(c1, 5);
|
|
||||||
final boolean isRemoved = isBitSet(c1, 7);
|
|
||||||
final boolean isImpedance = isBitSet(c1, 1);
|
|
||||||
|
|
||||||
if (!isStabilized || isRemoved) {
|
if (!isStabilized || isRemoved) {
|
||||||
LogManager.d(TAG, "Live ignored (unstable/removed)");
|
LogManager.d(TAG, "Live ignored (unstable/removed)");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int year = ((data[3] & 0xFF) << 8) | (data[4] & 0xFF);
|
final int year = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
|
||||||
final int month = (data[5] & 0xFF);
|
final int month = (data[4] & 0xFF);
|
||||||
final int day = (data[6] & 0xFF);
|
final int day = (data[5] & 0xFF);
|
||||||
final int hour = (data[7] & 0xFF);
|
final int hour = (data[6] & 0xFF);
|
||||||
final int minute = (data[8] & 0xFF);
|
final int minute = (data[7] & 0xFF);
|
||||||
|
|
||||||
int weightRaw = ((data[12] & 0xFF) << 8) | (data[11] & 0xFF);
|
int weightRaw = ((data[12] & 0xFF) << 8) | (data[11] & 0xFF);
|
||||||
float weight = (isLbs || isCatty) ? (weightRaw / 100.0f) : (weightRaw / 200.0f);
|
float weight = (isLbs || isCatty) ? (weightRaw / 100.0f) : (weightRaw / 200.0f);
|
||||||
@@ -199,7 +211,7 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
|||||||
float impedance = 0.0f;
|
float impedance = 0.0f;
|
||||||
if (isImpedance) {
|
if (isImpedance) {
|
||||||
impedance = ((data[10] & 0xFF) << 8) | (data[9] & 0xFF);
|
impedance = ((data[10] & 0xFF) << 8) | (data[9] & 0xFF);
|
||||||
LogManager.d(TAG, "Impedance: " + impedance + " Ω");
|
LogManager.d(TAG, "Impedance: " + impedance + " Ohm");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -208,7 +220,7 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
|||||||
|
|
||||||
if (!validateDate(dt, 20)) {
|
if (!validateDate(dt, 20)) {
|
||||||
LogManager.w(TAG, "Live date out of range: " + dt, null);
|
LogManager.w(TAG, "Live date out of range: " + dt, null);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ScaleUser user = getSelectedScaleUser();
|
final ScaleUser user = getSelectedScaleUser();
|
||||||
@@ -222,15 +234,17 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
|||||||
m.setWater(lib.getWater(weight, impedance));
|
m.setWater(lib.getWater(weight, impedance));
|
||||||
m.setVisceralFat(lib.getVisceralFat(weight));
|
m.setVisceralFat(lib.getVisceralFat(weight));
|
||||||
m.setFat(lib.getBodyFat(weight, impedance));
|
m.setFat(lib.getBodyFat(weight, impedance));
|
||||||
m.setMuscle(lib.getMuscle(weight, impedance)); // expects % from MiScaleLib
|
m.setMuscle(lib.getMuscle(weight, impedance));
|
||||||
m.setLbm(lib.getLBM(weight, impedance));
|
m.setLbm(lib.getLBM(weight, impedance));
|
||||||
m.setBone(lib.getBoneMass(weight, impedance));
|
m.setBone(lib.getBoneMass(weight, impedance));
|
||||||
}
|
}
|
||||||
|
|
||||||
addScaleMeasurement(m);
|
addScaleMeasurement(m);
|
||||||
LogManager.i(TAG, "Live saved @ " + dt + " kg=" + m.getWeight());
|
LogManager.i(TAG, "Live saved @ " + dt + " kg=" + m.getWeight());
|
||||||
|
return true;
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
setBluetoothStatus(UNEXPECTED_ERROR, "Live date parse error (" + e.getMessage() + ")");
|
setBluetoothStatus(UNEXPECTED_ERROR, "Live date parse error (" + e.getMessage() + ")");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,11 +276,11 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
|||||||
int weightRaw = ((data[2] & 0xFF) << 8) | (data[1] & 0xFF);
|
int weightRaw = ((data[2] & 0xFF) << 8) | (data[1] & 0xFF);
|
||||||
float weight = (isLbs || isCatty) ? (weightRaw / 100.0f) : (weightRaw / 200.0f);
|
float weight = (isLbs || isCatty) ? (weightRaw / 100.0f) : (weightRaw / 200.0f);
|
||||||
|
|
||||||
int year = ((data[4] & 0xFF) << 8) | (data[3] & 0xFF);
|
final int year = ((data[4] & 0xFF) << 8) | (data[3] & 0xFF);
|
||||||
int month = data[5] & 0xFF;
|
final int month = (data[5] & 0xFF);
|
||||||
int day = data[6] & 0xFF;
|
final int day = (data[6] & 0xFF);
|
||||||
int hour = data[7] & 0xFF;
|
final int hour = (data[7] & 0xFF);
|
||||||
int minute = data[8] & 0xFF;
|
final int minute = (data[8] & 0xFF);
|
||||||
// int second = data[9] & 0xFF;
|
// int second = data[9] & 0xFF;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -294,20 +308,35 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
|||||||
|
|
||||||
private void appendAndParseHistory(byte[] chunk) {
|
private void appendAndParseHistory(byte[] chunk) {
|
||||||
try {
|
try {
|
||||||
|
// Ignore 13-byte frames when in historyMode (they get parsed separately)
|
||||||
|
if (historyMode && (chunk.length == 13)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Ignore control/short frames
|
||||||
|
if (chunk.length < 10) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append raw chunk to buffer
|
||||||
histBuf.write(chunk, 0, chunk.length);
|
histBuf.write(chunk, 0, chunk.length);
|
||||||
byte[] buf = histBuf.toByteArray();
|
byte[] buf = histBuf.toByteArray();
|
||||||
|
|
||||||
|
// Process all complete 10-byte records in buffer
|
||||||
int full = (buf.length / 10) * 10;
|
int full = (buf.length / 10) * 10;
|
||||||
if (full >= 10) {
|
if (full >= 10) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
for (int off = 0; off < full; off += 10) {
|
for (int off = 0; off < full; off += 10) {
|
||||||
if (parseHistoryRecord10(Arrays.copyOfRange(buf, off, off + 10))) ok++;
|
byte[] rec = Arrays.copyOfRange(buf, off, off + 10);
|
||||||
|
if (parseHistoryRecord10(rec)) {
|
||||||
|
ok++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ok > 0) {
|
if (ok > 0) {
|
||||||
importedHistory += ok;
|
importedHistory += ok;
|
||||||
LogManager.d(TAG, "History parsed: " + ok + " record(s) from " + full + " bytes");
|
LogManager.d(TAG, "History parsed: " + ok + " record(s) from " + full + " bytes");
|
||||||
}
|
}
|
||||||
// keep remainder
|
|
||||||
|
// Reset buffer and keep any remainder
|
||||||
histBufReset();
|
histBufReset();
|
||||||
if (buf.length > full) {
|
if (buf.length > full) {
|
||||||
histBuf.write(buf, full, buf.length - full);
|
histBuf.write(buf, full, buf.length - full);
|
||||||
@@ -345,15 +374,4 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
|
|||||||
Calendar min = Calendar.getInstance(); min.add(Calendar.YEAR, -rangeYears);
|
Calendar min = Calendar.getInstance(); min.add(Calendar.YEAR, -rangeYears);
|
||||||
return weightDate.before(max.getTime()) && weightDate.after(min.getTime());
|
return weightDate.before(max.getTime()) && weightDate.after(min.getTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getUniqueNumber() {
|
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
int n = prefs.getInt("uniqueNumber", 0x00);
|
|
||||||
if (n == 0x00) {
|
|
||||||
n = new Random().nextInt(65535 - 100 + 1) + 100;
|
|
||||||
prefs.edit().putInt("uniqueNumber", n).apply();
|
|
||||||
}
|
|
||||||
int userId = getSelectedScaleUser().getId();
|
|
||||||
return n + userId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -169,23 +169,4 @@ public class BluetoothYunmaiSE_Mini extends BluetoothCommunication {
|
|||||||
|
|
||||||
addScaleMeasurement(scaleBtData);
|
addScaleMeasurement(scaleBtData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getUniqueNumber() {
|
|
||||||
int uniqueNumber;
|
|
||||||
|
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
|
|
||||||
uniqueNumber = prefs.getInt("uniqueNumber", 0x00);
|
|
||||||
|
|
||||||
if (uniqueNumber == 0x00) {
|
|
||||||
Random r = new Random();
|
|
||||||
uniqueNumber = r.nextInt(65535 - 100 + 1) + 100;
|
|
||||||
|
|
||||||
prefs.edit().putInt("uniqueNumber", uniqueNumber).apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
int userId = getSelectedScaleUser().getId();
|
|
||||||
|
|
||||||
return uniqueNumber + userId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -32,6 +32,8 @@ import com.health.openscale.core.bluetooth.data.ScaleMeasurement
|
|||||||
import com.health.openscale.core.bluetooth.data.ScaleUser
|
import com.health.openscale.core.bluetooth.data.ScaleUser
|
||||||
import com.health.openscale.core.data.MeasurementTypeKey
|
import com.health.openscale.core.data.MeasurementTypeKey
|
||||||
import com.health.openscale.core.database.DatabaseRepository
|
import com.health.openscale.core.database.DatabaseRepository
|
||||||
|
import com.health.openscale.core.database.UserSettingsRepository
|
||||||
|
import com.health.openscale.core.database.provideUserSettingsRepository
|
||||||
import com.health.openscale.core.utils.LogManager
|
import com.health.openscale.core.utils.LogManager
|
||||||
import kotlinx.coroutines.CoroutineName
|
import kotlinx.coroutines.CoroutineName
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -50,6 +52,7 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
import kotlin.random.Random
|
||||||
import kotlin.text.find
|
import kotlin.text.find
|
||||||
import kotlin.text.toDouble
|
import kotlin.text.toDouble
|
||||||
|
|
||||||
@@ -125,6 +128,18 @@ class LegacyScaleAdapter(
|
|||||||
|
|
||||||
LogManager.i(TAG, "Successfully provided ${userListFromDb.size} users to legacy driver ${bluetoothDriverInstance.driverName()}.")
|
LogManager.i(TAG, "Successfully provided ${userListFromDb.size} users to legacy driver ${bluetoothDriverInstance.driverName()}.")
|
||||||
|
|
||||||
|
val userSettingsRepository = provideUserSettingsRepository(applicationContext)
|
||||||
|
val keyName = "unique_number_base"
|
||||||
|
var base: Int = userSettingsRepository.observeSetting(keyName, 0).first()
|
||||||
|
if (base == 0) {
|
||||||
|
base = Random.nextInt(100, 65535)
|
||||||
|
userSettingsRepository.saveSetting(keyName, base)
|
||||||
|
LogManager.i(TAG, "Generated and saved unique_number_base=$base in DataStore")
|
||||||
|
} else {
|
||||||
|
LogManager.d(TAG, "Loaded unique_number_base=$base from DataStore")
|
||||||
|
}
|
||||||
|
|
||||||
|
bluetoothDriverInstance.setUniqueNumber(base)
|
||||||
this.currentInternalUser?.let { userId ->
|
this.currentInternalUser?.let { userId ->
|
||||||
if (userId.id != -1) {
|
if (userId.id != -1) {
|
||||||
LogManager.d(TAG, "Attempting to load last measurement for user ID: $userId using existing repository methods.")
|
LogManager.d(TAG, "Attempting to load last measurement for user ID: $userId using existing repository methods.")
|
||||||
|
Reference in New Issue
Block a user