1
0
mirror of https://github.com/oliexdev/openScale.git synced 2025-08-22 16:23:09 +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:
oliexdev
2025-08-20 08:36:34 +02:00
parent 01fd3214aa
commit 9dc61d7e53
5 changed files with 91 additions and 79 deletions

View File

@@ -83,6 +83,8 @@ public abstract class BluetoothCommunication {
private List<ScaleUser> cachedAppUserList;
protected ScaleMeasurement cachedLastMeasurementForSelectedUser;
private int uniqueBase = -1;
public BluetoothCommunication(Context context)
{
this.context = context;
@@ -129,6 +131,21 @@ public abstract class BluetoothCommunication {
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) {
if (callbackBtHandler != null) {
Message msg = callbackBtHandler.obtainMessage(BT_STATUS.USER_INTERACTION_REQUIRED.ordinal());

View File

@@ -223,23 +223,4 @@ public class BluetoothMiScale extends BluetoothCommunication {
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;
}
}

View File

@@ -49,6 +49,8 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
private int pendingHistoryCount = -1;
private int importedHistory = 0;
private boolean historyMode = false;
// warn only once per session if we see "unexpected" flags in history status byte
private boolean historyUnexpectedFlagWarned = false;
@@ -65,6 +67,8 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
// STOP (end of history transfer)
if (value.length == 1 && value[0] == 0x03) {
historyMode = false;
// Check for truncated leftover BEFORE flush (for debug visibility)
int leftoverLen = histBuf.size();
if (leftoverLen % 10 != 0) {
@@ -95,15 +99,27 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
// Live frame (13 bytes)
if (value.length == 13) {
parseLiveFrame(value);
return;
if (historyMode) {
if (parseLiveFrame(value)) {
importedHistory++;
LogManager.d(TAG, "History (13B) parsed");
}
return;
} else {
parseLiveFrame(value);
return;
}
}
// Rare: two live frames combined in one notify (2x13)
if (value.length == 26) {
LogManager.d(TAG, "26-byte payload -> split into 2x13 live frames");
parseLiveFrame(Arrays.copyOfRange(value, 0, 13));
parseLiveFrame(Arrays.copyOfRange(value, 13, 26));
boolean s1 = parseLiveFrame(Arrays.copyOfRange(value, 0, 13));
boolean s2 = parseLiveFrame(Arrays.copyOfRange(value, 13, 26));
if (historyMode) {
int inc = (s1 ? 1 : 0) + (s2 ? 1 : 0);
if (inc > 0) importedHistory += inc;
}
return;
}
@@ -160,6 +176,7 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
case 4: { // trigger history transfer
writeBytes(BluetoothGattUuid.SERVICE_BODY_COMPOSITION, WEIGHT_MEASUREMENT_HISTORY_CHARACTERISTIC, new byte[]{0x02});
LogManager.d(TAG, "History transfer triggered (0x02)");
historyMode = true;
stopMachineState(); // wait for notifications
break;
}
@@ -171,27 +188,22 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
// --- Parsing ---
private void parseLiveFrame(byte[] data) {
// ctrl bytes
final byte c0 = data[0];
final byte c1 = data[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);
private boolean parseLiveFrame(byte[] data) {
final byte c0 = data[0], c1 = data[1];
final boolean isLbs = isBitSet(c0,0), isCatty = isBitSet(c1,6),
isStabilized = isBitSet(c1,5), isRemoved = isBitSet(c1,7),
isImpedance = isBitSet(c1,1);
if (!isStabilized || isRemoved) {
LogManager.d(TAG, "Live ignored (unstable/removed)");
return;
return false;
}
final int year = ((data[3] & 0xFF) << 8) | (data[4] & 0xFF);
final int month = (data[5] & 0xFF);
final int day = (data[6] & 0xFF);
final int hour = (data[7] & 0xFF);
final int minute = (data[8] & 0xFF);
final int year = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
final int month = (data[4] & 0xFF);
final int day = (data[5] & 0xFF);
final int hour = (data[6] & 0xFF);
final int minute = (data[7] & 0xFF);
int weightRaw = ((data[12] & 0xFF) << 8) | (data[11] & 0xFF);
float weight = (isLbs || isCatty) ? (weightRaw / 100.0f) : (weightRaw / 200.0f);
@@ -199,7 +211,7 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
float impedance = 0.0f;
if (isImpedance) {
impedance = ((data[10] & 0xFF) << 8) | (data[9] & 0xFF);
LogManager.d(TAG, "Impedance: " + impedance + " Ω");
LogManager.d(TAG, "Impedance: " + impedance + " Ohm");
}
try {
@@ -208,7 +220,7 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
if (!validateDate(dt, 20)) {
LogManager.w(TAG, "Live date out of range: " + dt, null);
return;
return false;
}
final ScaleUser user = getSelectedScaleUser();
@@ -222,15 +234,17 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
m.setWater(lib.getWater(weight, impedance));
m.setVisceralFat(lib.getVisceralFat(weight));
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.setBone(lib.getBoneMass(weight, impedance));
}
addScaleMeasurement(m);
LogManager.i(TAG, "Live saved @ " + dt + " kg=" + m.getWeight());
return true;
} catch (ParseException e) {
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);
float weight = (isLbs || isCatty) ? (weightRaw / 100.0f) : (weightRaw / 200.0f);
int year = ((data[4] & 0xFF) << 8) | (data[3] & 0xFF);
int month = data[5] & 0xFF;
int day = data[6] & 0xFF;
int hour = data[7] & 0xFF;
int minute = data[8] & 0xFF;
final int year = ((data[4] & 0xFF) << 8) | (data[3] & 0xFF);
final int month = (data[5] & 0xFF);
final int day = (data[6] & 0xFF);
final int hour = (data[7] & 0xFF);
final int minute = (data[8] & 0xFF);
// int second = data[9] & 0xFF;
try {
@@ -294,20 +308,35 @@ public class BluetoothMiScale2 extends BluetoothCommunication {
private void appendAndParseHistory(byte[] chunk) {
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);
byte[] buf = histBuf.toByteArray();
// Process all complete 10-byte records in buffer
int full = (buf.length / 10) * 10;
if (full >= 10) {
int ok = 0;
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) {
importedHistory += ok;
LogManager.d(TAG, "History parsed: " + ok + " record(s) from " + full + " bytes");
}
// keep remainder
// Reset buffer and keep any remainder
histBufReset();
if (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);
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;
}
}

View File

@@ -169,23 +169,4 @@ public class BluetoothYunmaiSE_Mini extends BluetoothCommunication {
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;
}
}

View File

@@ -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.data.MeasurementTypeKey
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 kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
@@ -50,6 +52,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import java.lang.ref.WeakReference
import java.util.Date
import kotlin.random.Random
import kotlin.text.find
import kotlin.text.toDouble
@@ -125,6 +128,18 @@ class LegacyScaleAdapter(
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 ->
if (userId.id != -1) {
LogManager.d(TAG, "Attempting to load last measurement for user ID: $userId using existing repository methods.")