#worth looking at https github com hbldh

1 messages · Page 1 of 1 (latest)

onyx zenith
#

This is great!

Got it connected and got some responses

#
[Characteristic] 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 18): Vendor specific (notify)
[Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 20): Client Characteristic Configuration, Value: bytearray(b'\x00\x00')
[Characteristic] 0000ffe2-0000-1000-8000-00805f9b34fb (Handle: 21): Vendor specific (indicate)
[Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 23): Client Characteristic Configuration, Value: bytearray(b'\x00\x00')
[Characteristic] 0000ffe3-0000-1000-8000-00805f9b34fb (Handle: 24): Vendor specific (write-without-response,write)
[Characteristic] 0000ffe4-0000-1000-8000-00805f9b34fb (Handle: 26): Vendor specific (write-without-response,write)
[Characteristic] 0000ffe5-0000-1000-8000-00805f9b34fb (Handle: 28): Vendor specific (write-without-response,write)
[Service] 0000180f-0000-1000-8000-00805f9b34fb (Handle: 30): Battery Service```
wintry trellis
#

It loooks like discord has swallowed the formatting

onyx zenith
#

Better now?

wintry trellis
#

Don’t know if it’s the phone client or not but doesn’t it normally have indentation so you can see the relationships a bit better

onyx zenith
#

Yes you are right

#
  [Characteristic] 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 18): Vendor specific (notify)
    [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 20): Client Characteristic Configuration, Value: bytearray(b'\x00\x00')
  [Characteristic] 0000ffe2-0000-1000-8000-00805f9b34fb (Handle: 21): Vendor specific (indicate)
    [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 23): Client Characteristic Configuration, Value: bytearray(b'\x00\x00')
  [Characteristic] 0000ffe3-0000-1000-8000-00805f9b34fb (Handle: 24): Vendor specific (write-without-response,write)
  [Characteristic] 0000ffe4-0000-1000-8000-00805f9b34fb (Handle: 26): Vendor specific (write-without-response,write)
  [Characteristic] 0000ffe5-0000-1000-8000-00805f9b34fb (Handle: 28): Vendor specific (write-without-response,write)```
wintry trellis
#

Lovely

onyx zenith
#

There are 3 other sections:

Generic Access Profile
Device Information
Battery Service

wintry trellis
#

So it’s worth reviewing all of them

#

We are interesting in read and notify stuff

onyx zenith
#
  [Characteristic] 00002a00-0000-1000-8000-00805f9b34fb (Handle: 2): Device Name (read), Value: bytearray(b'QN-Scale\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
  [Characteristic] 00002a01-0000-1000-8000-00805f9b34fb (Handle: 4): Appearance (read), Value: bytearray(b'\x00\x00')
  [Characteristic] 00002a04-0000-1000-8000-00805f9b34fb (Handle: 6): Peripheral Preferred Connection Parameters (read), Value: bytearray(b'\x14\x00(\x00\x00\x00\xe8\x03')
[Service] 0000180a-0000-1000-8000-00805f9b34fb (Handle: 8): Device Information
  [Characteristic] 00002a50-0000-1000-8000-00805f9b34fb (Handle: 9): PnP ID (read), Value: bytearray(b'\x02\x8a$f\x82\x01\x00')
  [Characteristic] 00002a29-0000-1000-8000-00805f9b34fb (Handle: 11): Manufacturer Name String (read), Value: bytearray(b'Qing Niu Technology')
  [Characteristic] 00002a26-0000-1000-8000-00805f9b34fb (Handle: 13): Firmware Revision String (read), Value: bytearray(b'V56.0')
  [Characteristic] 00002a23-0000-1000-8000-00805f9b34fb (Handle: 15): System ID (read), Value: bytearray(b'#\x91\xf1\x00\x008\xc1\xa4')
[Service] 0000180f-0000-1000-8000-00805f9b34fb (Handle: 30): Battery Service
  [Characteristic] 00002a19-0000-1000-8000-00805f9b34fb (Handle: 31): Battery Level (read), Value: bytearray(b'\x00')```
wintry trellis
#

If there’s anything “read”, does it correlate with a value you know

#

\x00 is a weird battery level

onyx zenith
#

All values seem to be that Byte Array

wintry trellis
#

\x00 is just 0

#

I’m guessing its not nearly flat?

onyx zenith
#

No, batteries are relatively new

wintry trellis
#

Anyway it’s probably worth trying the notify example with characteristic handle 18

onyx zenith
#

But also the Vendor specific section also shows the same \x00

onyx zenith
wintry trellis
#

I haven’t used it before but yes I think so

#

Notifications are tied to the connection

#

Which you say ends after 10s

#

So it might be annoying

#

Timing wise

onyx zenith
#

So I use this UUID: 0000ffe1-0000-1000-8000-00805f9b34fb

#

This was the notify Characteristic

2023-08-21 21:26:12,730 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-21 21:26:13,061 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-21 21:26:13,345 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-21 21:26:13,750 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-21 21:26:14,020 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-21 21:26:14,336 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-21 21:26:14,650 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-21 21:26:14,965 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-21 21:26:15,326 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-21 21:26:15,640 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-21 21:26:15,910 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-21 21:26:16,225 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-21 21:26:16,541 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-21 21:26:16,856 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-21 21:26:17,170 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')```
#

Got this

#

Funny thing is that I get a connected response, but the Bluetooth logo never appears on the scale

#

Also these values never changed

#

The indicate Characteristic returns nothing.

2023-08-21 21:34:08,610 __main__ INFO: connecting to device...
2023-08-21 21:34:12,476 __main__ INFO: Connected```
wintry trellis
#

Sorry, real life happened. I can have another look when I’ve got a bigger screen

#

All I’ve got is (a) it’s not BLE GATT. Maybe the old serial Bluetooth (not BLE) (b) we missed a read characteristic earlier (c) we missed a notify characteristic earlier or (d) what it is advertising does change and we missed that

#

Also googling uuids can be helpful

onyx zenith
#

Okay cool - Let me decompile the mobile app and see what I can find there

#

Seeing some GATT and BLE references:

            UUID uuid = BleConst.UUID_IBT_SERVICES_1;
            boolean z = bluetoothGatt.getService(uuid) != null;
            ScaleBleManager scaleBleManager = ScaleBleManager.this;
            BluetoothGattCharacteristic unused = scaleBleManager.batteryBgc = scaleBleManager.getCharacteristic(bluetoothGatt, BleConst.UUID_BATTERY_INFO_SERVICE, BleConst.UUID_BATTERY_INFO_READER);
            if (z) {
                ((ScaleBleManagerCallback) ScaleBleManager.this.f9978a).setHoltek();
                ScaleBleManager scaleBleManager2 = ScaleBleManager.this;
                BluetoothGattCharacteristic unused2 = scaleBleManager2.yolandaReadBgc = scaleBleManager2.getCharacteristic(bluetoothGatt, uuid, BleConst.UUID_IBT_READ_1);
                ScaleBleManager scaleBleManager3 = ScaleBleManager.this;
                BluetoothGattCharacteristic unused3 = scaleBleManager3.yolandaWriteBgc = scaleBleManager3.getCharacteristic(bluetoothGatt, uuid, BleConst.UUID_IBT_WRITE_1);
                BluetoothGattService service = bluetoothGatt.getService(UUID.fromString("0000180a-0000-1000-8000-00805f9b34fb"));
                if (service != null) {
                    BluetoothGattCharacteristic unused4 = ScaleBleManager.this.readSnBgc = service.getCharacteristic(UUID.fromString("00002a25-0000-1000-8000-00805f9b34fb"));
                }
            }```
onyx zenith
#

I am also seeing more UUIDs in the code here:


public interface WSPBleConst {
    public static final String UUID_BODY_NOTIFY = "00002a9c-0000-1000-8000-00805f9b34fb";
    public static final String UUID_BODY_READ = "00002a9b-0000-1000-8000-00805f9b34fb";
    public static final String UUID_SN_READ = "00002a25-0000-1000-8000-00805f9b34fb";
    public static final String UUID_SN_SERVICES = "0000180a-0000-1000-8000-00805f9b34fb";
    public static final String UUID_TIME_SERVICES = "00001805-0000-1000-8000-00805f9b34fb";
    public static final String UUID_TIME_WRITE_READ = "00002a2b-0000-1000-8000-00805f9b34fb";
    public static final String UUID_USER_AGE_UPDATE = "00002a80-0000-1000-8000-00805f9b34fb";
    public static final String UUID_USER_ALGORITHM_UPDATE = "00002aff-0000-1000-8000-00805f9b34fb";
    public static final String UUID_USER_BIRTHDAY_UPDATE = "00002a85-0000-1000-8000-00805f9b34fb";
    public static final String UUID_USER_DEFINED_READ = "0000ffe1-0000-1000-8000-00805f9b34fb";
    public static final String UUID_USER_DEFINED_WRITE = "0000ffe2-0000-1000-8000-00805f9b34fb";
    public static final String UUID_USER_GRAND_UPDATE = "00002a8c-0000-1000-8000-00805f9b34fb";
    public static final String UUID_USER_HEIGHT_UPDATE = "00002a8e-0000-1000-8000-00805f9b34fb";
    public static final String UUID_USER_SERVICES = "0000181c-0000-1000-8000-00805f9b34fb";
    public static final String UUID_USER_WRITE_READ = "00002a9f-0000-1000-8000-00805f9b34fb";
    public static final String UUID_WEIGHT_NOTIFY = "00002a9d-0000-1000-8000-00805f9b34fb";
    public static final String UUID_WEIGHT_READ = "00002a9e-0000-1000-8000-00805f9b34fb";
    public static final String UUID_WEIGHT_SERVICES = "0000181d-0000-1000-8000-00805f9b34fb";
    public static final String UUID_WIFI_READ = "0000fff1-0000-1000-8000-00805f9b34fb";
    public static final String UUID_WIFI_WRITE = "0000fff2-0000-1000-8000-00805f9b34fb";
}```
#

I've tried:
UUID_WEIGHT_NOTIFY
UUID_WEIGHT_READ
UUID_WEIGHT_SERVICES

But I get an error that the characteristic can't be found...

wintry trellis
#

Can you find any code that uses them?

#

Do any of those match what we found with service browser?

onyx zenith
#

Found this:

            if ((decodeCompanyID(scanResult.getScanRecord()) == Integer.parseInt("FFFF", 16)) && (serviceUuids = scanResult.getScanRecord().getServiceUuids()) != null && ((serviceUuids.contains(new ParcelUuid(UUID.fromString(FascialGunConst.UUID_FASCIAL_GUN_SERVICE))) || serviceUuids.contains(new ParcelUuid(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb")))) && (valueAt = manufacturerSpecificData.valueAt(0)) != null)) {
                if (serviceUuids.contains(new ParcelUuid(UUID.fromString(FascialGunConst.UUID_FASCIAL_GUN_SERVICE)))) {
                    if (valueAt.length >= 14) {
                        fascialGunDevice = new FascialGunDevice();
                        fascialGunDevice.setInternalModel(String.format("%02X%02X", new Object[]{Byte.valueOf(valueAt[0]), Byte.valueOf(valueAt[1])}));
                        fascialGunDevice.setBluetoothName(scanResult.getTruthLocalName());
                        fascialGunDevice.setMac(scanResult.getMac());
                        fascialGunDevice.setFascialGunType(ConvertUtils.twoByte2Int(valueAt[8], valueAt[9]));
                        fascialGunDevice.setFirmwareVer(ConvertUtils.twoByte2Int(valueAt[10], valueAt[11]));
                        fascialGunDevice.setProtVer(ConvertUtils.twoByte2Int(valueAt[12], valueAt[13]));
                        fascialGunDevice.setGunType(GunType.decodeGunType(fascialGunDevice.getFascialGunType()));
                    }
                }
                QNLogUtils.logAndWrite(TAG, "发现筋膜枪 " + fascialGunDevice);
            }```
#
        WspCmd wspCmd = new WspCmd();
        wspCmd.setUuid(WSPBleConst.UUID_WEIGHT_NOTIFY);
        QNLogUtils.logAndWrite(TAG, "使能体重数据命令");
        this.scaleWspBleManager.addWspCmd(wspCmd);
    }

    public void readWeightSupport() {
        WspCmd wspCmd = new WspCmd();
        wspCmd.setUuid(WSPBleConst.UUID_WEIGHT_READ);
        QNLogUtils.logAndWrite(TAG, "读取体重支持特征命令");
        this.scaleWspBleManager.addWspCmd(wspCmd);
    }```
#
        WspCmd wspCmd = new WspCmd();
        wspCmd.setUuid(WSPBleConst.UUID_USER_DEFINED_WRITE);
        ArrayList arrayList = new ArrayList();
        arrayList.add((byte) 3);
        arrayList.add((byte) 1);
        arrayList.add((byte) 0);
        int i = 0;
        for (int i2 = 0; i2 < arrayList.size(); i2++) {
            i += ((Byte) arrayList.get(i2)).byteValue();
        }
        arrayList.add(Byte.valueOf((byte) i));
        wspCmd.setData(ConvertUtils.listToBytes(arrayList));
        QNLogUtils.logAndWrite(TAG, "发送读取体重单位命令:" + ConvertUtils.bytesToHexStr(ConvertUtils.listToBytes(arrayList)));
        this.scaleWspBleManager.addWspCmd(wspCmd);
    }```
wintry trellis
#

so if you haven't already, i would

#

work your way through the UUID's your device does have

#

and grep the code for them

#

for example, the one that we used with the notify code is in this app:

#

public static final String UUID_USER_DEFINED_READ = "0000ffe1-0000-1000-8000-00805f9b34fb";

#

so now i'm very interested in where UUID_USER_DEFINED_READ is used

#

UUID_USER_DEFINED_WRITE is the indicate characteristic we talked about, so im interested in that. you posted one example of that. is that the only thing that uses it? what calls that function?

onyx zenith
#

This gets used quite often,

Seems like when writing Data back to the device?

        WspCmd wspCmd = new WspCmd();
        wspCmd.setUuid(WSPBleConst.UUID_USER_DEFINED_WRITE);
        ArrayList arrayList = new ArrayList();
        arrayList.add((byte) 3);
        arrayList.add((byte) 0);
        arrayList.add(Byte.valueOf((byte) i));
        int i2 = 0;
        for (int i3 = 0; i3 < arrayList.size(); i3++) {
            i2 += ((Byte) arrayList.get(i3)).byteValue();
        }
        arrayList.add(Byte.valueOf((byte) i2));
        wspCmd.setData(ConvertUtils.listToBytes(arrayList));
        QNLogUtils.logAndWrite(TAG, "发送设置体重单位命令:" + ConvertUtils.bytesToHexStr(ConvertUtils.listToBytes(arrayList)));
        this.scaleWspBleManager.addWspCmd(wspCmd);
    }```
#
        WspCmd wspCmd = new WspCmd();
        wspCmd.setUuid(WSPBleConst.UUID_USER_DEFINED_WRITE);
        int i2 = (int) (d / 0.05d);
        ArrayList arrayList = new ArrayList();
        arrayList.add((byte) 7);
        arrayList.add(Byte.valueOf((byte) i));
        arrayList.add(Byte.valueOf((byte) (i2 & 255)));
        arrayList.add(Byte.valueOf((byte) ((i2 >> 8) & 255)));
        int i3 = 0;
        for (int i4 = 0; i4 < arrayList.size(); i4++) {
            i3 += ((Byte) arrayList.get(i4)).byteValue();
        }
        arrayList.add(Byte.valueOf((byte) i3));
        wspCmd.setData(ConvertUtils.listToBytes(arrayList));
        QNLogUtils.logAndWrite(TAG, "更新用户最新测量记录:" + ConvertUtils.bytesToHexStr(ConvertUtils.listToBytes(arrayList)));
        this.scaleWspBleManager.addWspCmd(wspCmd);
    }```
wintry trellis
#

first log message: Send the Set Weight Unit command

onyx zenith
#

This one is quite interesting:

                ScaleWspBleManager scaleWspBleManager12 = ScaleWspBleManager.this;
                BluetoothGattCharacteristic unused16 = scaleWspBleManager12.bodyNotify = scaleWspBleManager12.bodyService.getCharacteristic(UUID.fromString(WSPBleConst.UUID_BODY_NOTIFY));
                ScaleWspBleManager scaleWspBleManager13 = ScaleWspBleManager.this;
                BluetoothGattCharacteristic unused17 = scaleWspBleManager13.bodyRead = scaleWspBleManager13.bodyService.getCharacteristic(UUID.fromString(WSPBleConst.UUID_BODY_READ));
                ScaleWspBleManager scaleWspBleManager14 = ScaleWspBleManager.this;
                BluetoothGattCharacteristic unused18 = scaleWspBleManager14.userDefinedRead = scaleWspBleManager14.bodyService.getCharacteristic(UUID.fromString(WSPBleConst.UUID_USER_DEFINED_READ));
                ScaleWspBleManager scaleWspBleManager15 = ScaleWspBleManager.this;
                BluetoothGattCharacteristic unused19 = scaleWspBleManager15.userDefinedWrite = scaleWspBleManager15.bodyService.getCharacteristic(UUID.fromString(WSPBleConst.UUID_USER_DEFINED_WRITE));
            }```

So it first does Notify, then Read, then Write
wintry trellis
#

second log message: Update the user's latest measurement history

onyx zenith
#

Aaah that is cool!

wintry trellis
#

so that just looks like getting handles to characteristics to use later

#

what it calls UUID_USER_DEFINED_READ

#

is the notify charactersitic

#

which we can't read (it doesnt have the read bit set)

#

are there any references to unused18 and unused19?

onyx zenith
#

No not really, some UI references only

wintry trellis
#

scaleWspBleManager15.userDefinedWrite and scaleWspBleManager14.userDefinedRead?

onyx zenith
#

Nothing!

#

Only those two lines

wintry trellis
#

proably an artifact of the decompiler but, but i'd guess ScaleWspBleManager.this.userDefinedRead and ScaleWspBleManager.this.userDefinedWrite were getting set indirectly through those references

onyx zenith
#

Yeah you probably right

wintry trellis
#

so anyhwere it does ScaleWspBleManager scaleWspBleManager??? = ScaleWspBleManager.this;

#

might then have a scaleWspBleManager???.userDefinedWrite / userDefinedRead

#

so you are probably better search for userDefinedWrite/userDefinedRead

onyx zenith
#

There is this part that is commented out:

            android.bluetooth.BluetoothGattCharacteristic r6 = r5.userDefinedWrite
            if (r6 == 0) goto L_0x0157
            r6.setValue(r7)
            android.bluetooth.BluetoothGattCharacteristic r6 = r5.userDefinedWrite
            goto L_0x0152```
#
                com.qingniu.scale.wsp.ble.ScaleWspBleManager r0 = com.qingniu.scale.wsp.ble.ScaleWspBleManager.this
                android.bluetooth.BluetoothGattCharacteristic r0 = r0.userDefinedRead
                if (r0 == 0) goto L_0x006a
            L_0x005d:
                com.qingniu.scale.wsp.ble.ScaleWspBleManager r0 = com.qingniu.scale.wsp.ble.ScaleWspBleManager.this
                android.bluetooth.BluetoothGattCharacteristic r0 = r0.userDefinedRead
                com.qingniu.qnble.blemanage.profile.BleManager$Request r0 = com.qingniu.qnble.blemanage.profile.BleManager.Request.newEnableNotificationsRequest(r0)
                r2.add(r0)```
#

Which gets referenced often

#

Could I not perhaps send you the decompiled code?

For a quick search from your side?

wintry trellis
#

sure

#

luckily im at a computer

onyx zenith
#

Contains a ZIP file where the APK was decompiled to.

This app has loads of skins it seems, but the main folder to focus around is the sources\com\qingniu\scale\wsp\ble\

wintry trellis
#

ah

#

so the commented out stuff was commented with this, right?

#

/* Code decompiled incorrectly, please refer to instructions dump. */

onyx zenith
#

Ah yeah saw those - Not sure how else to decompile it...

wintry trellis
#

interesting

#

so there are multiple devices that have userDefined read/write

#

if we look for 0000ffe0-0000-1000-8000-00805f9b34fb, which we got from your device

onyx zenith
#

Yeah I think there are multiple Apps under this same Code Base

wintry trellis
#

its not the one with BODY_NOTIFY

#

it apparently has wifi

#
            if (ScaleWspBleManager.this.customerService != null) {
                ScaleWspBleManager scaleWspBleManager17 = ScaleWspBleManager.this;
                BluetoothGattCharacteristic unused23 = scaleWspBleManager17.userDefinedRead = scaleWspBleManager17.customerService.getCharacteristic(UUID.fromString(WSPBleConst.UUID_USER_DEFINED_READ));
                ScaleWspBleManager scaleWspBleManager18 = ScaleWspBleManager.this;
                BluetoothGattCharacteristic unused24 = scaleWspBleManager18.userDefinedWrite = scaleWspBleManager18.customerService.getCharacteristic(UUID.fromString(WSPBleConst.UUID_USER_DEFINED_WRITE));
                ScaleWspBleManager scaleWspBleManager19 = ScaleWspBleManager.this;
                BluetoothGattCharacteristic unused25 = scaleWspBleManager19.wifiRead = scaleWspBleManager19.userDefinedRead;
                ScaleWspBleManager scaleWspBleManager20 = ScaleWspBleManager.this;
                BluetoothGattCharacteristic unused26 = scaleWspBleManager20.wifiWrite = scaleWspBleManager20.userDefinedWrite;
            }```
onyx zenith
#

Yeah my device definitely does not have Wifi

#

I see that UUID is also inside sources\com\qingniu\qnble\utils\BleUtils.java

#

But it did not compile correctly

wintry trellis
#

so looking at writeCmd

#

it sets this.latestCmdUUID to r6, which is a string. and the instruction dump looks like a big if/else if/else if table

#

its comparing r6 to a bunch of uuids to figure out hwat to do

#

looking at places that use writeCmd, addWspCmd is one of them

#

so the readWeight function uses addWspCommand with 00002a9d-0000-1000-8000-00805f9b34fb, which calls writeCmd, which checks for that same uuid and jumps to L_0x0010 and then seems to use a variable thats always 0 to pick a characteristic to actually write to

#

if we assume its supposed to use our characteristic

#

UUID_USER_DEFINED_WRITE

#

well the readWeight function doesnt set a payload on wspcmd

onyx zenith
#

That is the Indicate characteristic right

wintry trellis
#

so i think it would write an empty string to the write characteristic

#

which baffles me

onyx zenith
#

It does seem strange.

One other thing to note, when I use the official APP, I also get a LIVE reading of the weight.

Then once the weight has stabalized, then I get the getStableWeightData

#

So there is some sort of direct connection during the weighing period, then it ends that with sending the stable weight and then disconnects

wintry trellis
#

theres another mis-decompiled bit where it sets up the notifcations

#

but it looks like sometimes when it gets data it will call onReceiveData and then call a function called continueWrite

#

so i wonder if thats meant to ACK the data you see

#

and you dont see any more data because we are not ACKing anything

onyx zenith
#

Yeah so perhaps the Scale needs an ACK in order to show the Bluetooth icon

wintry trellis
#

it looks like the "meat" of understanding the notify callback is in QNDecoderImpl.java

onyx zenith
#

Which seems to be like correctly decompiled code

#

Even the Logs are in english:
QNLogUtils.logAndWrite(TAG, "Data discarded if the measurement time stored is incorrect:" + ((bArr[3] >> 4) & 15) + "-" + (bArr[3] & 15));

wintry trellis
#

brilliant

#

so writeCmd

#

weight read sets c2 to 6

#

which sets the char to weightRead and doesnt do anything

#

if you can run the actual app and get the debug logs you might be able to map it to the functions in the code

#

shrug

#

otherwise you need to follow the characteristics you have backwards

#

from this link we know writeCmd only calls userDefinedWrite (which we have on our device) if writeCmd is called with UUID_USER_WRITE_READ

#

if writeCmd is called with 0000ffe2-0000-1000-8000-00805f9b34fb

#

which is just UUID_USER_DEFINED_WRITE

#

so its just a tedious java thing

onyx zenith
#

Okay I see what you mean

#

So could I perhaps use Bleak to write to that UUID?

#

What would happen?

wintry trellis
#

no idea

#

so we need to look at users of writeCmd or addWspCmd that use UUID_USER_DEFINED_WRITE or 0000ffe2-0000-1000-8000-00805f9b34fb

#

and they'll have a data payload that might represent a question

#

which takes us back to those arrayList functions you already found

onyx zenith
#

Okay, how would I go about getting the debug logs of the App?

wintry trellis
#

no idea

#

there are 21 occurences of wspCmd.setUuid(WSPBleConst.UUID_USER_DEFINED_WRITE); in WspSendManager.jaa

#

all of those are commands that would be written to the indicate characteristic on your device

onyx zenith
#

writeWeightUnit seems likely

wintry trellis
#

i was looking at readWeightUnit

onyx zenith
#

Ah yeah probably that

wintry trellis
#

given there is a pair

#

and the write path glares seems to cast the int to a byte (so 0-255)

#

i guess it depends on the scales and their max weight and their precision

#

if they were 0.5kg precision you could weigh 128kg of person in a byte? i guess

#

but i wonder if htats actually the lb vs kg switch

#

you can read the unit or set the unit

#

makes more sense that setting the weight

onyx zenith
#

Precision seems to be 0.05 KG

wintry trellis
#

yeah no way thats a byte

onyx zenith
#

Max Weight is also 150KG

wintry trellis
#

can you see your gender on the scales?

#

oh weight thats not user_defined_write nm

#

so i noticed an onDeviceReady callback that calls writeWeightUnit

#

theres a transUnit thats.. annoying

#

if the unit is 8 then return 3, if the units 16, return 5 or 2, otherwise use the original value...

#

i was hoping we could try flipping you from lbs to kgs 😄

#

theres som interesting callbacks in here that seem to udpdate ui - onGetRealTimeWeight

#

onGetData

#

they seem to get called from the decoders

#

but the app has multiple decoders

#

interesting

#

updateScaleMeasureRecord

#

has this:

#

int i2 = (int) (d / 0.05d);

#

for turning a double into an integer

#

which it spreads over 2 bytes

#

anyway you should be able to grep for a method in WspSendManager, manually construct a buffer for it, then write to it from python

#

e.g. notifyScaleDelayDisconnectTime is [6,1,0] and the last byte is some sort of checksum. i think its 7 in this case. so from python you can just write b"\x06\x01\x00\x07"

#

apparently that will "Notify the weighing screen of the connection time"

onyx zenith
#

Wow okay that is cool.

So when you say grep for a method, do i send a command as that UUID?

#

I see it is using UUID_USER_DEFINED_WRITE

wintry trellis
#

yeah so there are 22 cases of this:

#

wspCmd.setUuid(WSPBleConst.UUID_USER_DEFINED_WRITE);

#

when you see that, you can write the same payload to the 0000ffe2-char

onyx zenith
#

So I have this:

from bleak import BleakClient

address = "A4:C1:38:F1:91:23"
notifyID = "0000ffe2-0000-1000-8000-00805f9b34fb"

async def main(address):
    async with BleakClient(address) as client:
        array_list = []
        array_list.append(3)
        array_list.append(1)
        array_list.append(0)
        i = 0
        for value in array_list:
            i += value
        array_list.append(i)
        
        print(array_list)

        notify = await client.write_gatt_char(notifyID, b'\x06\x01\x00\x07')
        print("write: {0}".format(notify))

asyncio.run(main(address))```
onyx zenith
wintry trellis
#

great

#

maybe enable notifications on the other characteristic before the write?

onyx zenith
#

Okay so I start Notify on the notify characteristic, then I write the value to the indicate characteristic which returns None

#
2023-08-22 15:49:45,080 __main__ INFO: connecting to device...
2023-08-22 15:49:47,313 __main__ INFO: Connected
[3, 1, 0, 4]
2023-08-22 15:49:47,356 __main__ INFO: None
2023-08-22 15:49:47,668 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-22 15:49:47,983 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-22 15:49:48,299 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-22 15:49:48,674 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')
2023-08-22 15:49:49,033 __main__ INFO: Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')```
wintry trellis
#

did you start the notify before writing?

onyx zenith
#

Yes

#
        logger.info("Connected")

        await client.start_notify(args.characteristic_notify, notification_handler)
        array_list = []
        array_list.append(3)
        array_list.append(1)
        array_list.append(0)
        i = 0
        for value in array_list:
            i += value
        array_list.append(i)
        
        print(array_list)

        write = await client.write_gatt_char(args.characteristic_indicate, bytearray(array_list))
        logger.info(write)
        
        await asyncio.sleep(10.0)
        
        await client.stop_notify(args.characteristic_notify)```
wintry trellis
#

did the device do anything different?

onyx zenith
#

Seems exactly the same...

wintry trellis
#

what if you change the checksum

#

make it deliberately wrong

onyx zenith
#

bytearray(b'\x14\x01\x00\x15')

#

Think this is not correct

#

This is what I get as my Bytarray

onyx zenith
#

I've just hardcoded the bytearray for now

wintry trellis
#

im expecting None tbh

#

im epxecting the result to come from the notification handler

#

or for no response at all

onyx zenith
#

I am just getting the same response the entire time.

#

Vendor specific: bytearray(b'\x12\x0f\x15#\x91\xf18\xc1\xa48\x018\x00\x00\xe9')

wintry trellis
#

i mean you could go away and try and parse that

#

theres 3 decoders in the source, not sure which one it is

#

e.g. QNDecoder.decodeData takes a UUID and a byte array

onyx zenith
#

But from the method here, they are simply doing this:
QNLogUtils.logAndWrite(TAG, "发送读取体重单位命令:" + ConvertUtils.bytesToHexStr(ConvertUtils.listToBytes(arrayList)));

#

ConvertUtils.bytesToHexStr

wintry trellis
#

right

#

but they aren't dealing with any response here are they

#

they just spaff some bytes into a characteristic and run away

onyx zenith
#

Aaah yeah makes sense

wintry trellis
#

the only data we get from the device is that notify data, and the decoders like QNDecoder seem to handle those callbacks

#

so the first byte of your payload is 18

#

looking at decodeData i think thats this bit

#

its 15 bytes

#

easy stuff

#

foo[10] & 1 == 1, so weight ration is 100.0 not 10.0

onyx zenith
#

I see

wintry trellis
#

isSupportSt looks like it would be false

#

((bArr2[10] >> 1) & 1) == 1 || ((bArr2[10] >> 2) & 1) == 1

onyx zenith
#

So I kinda need to write that over to a Python script then to decode right

wintry trellis
#

exactly

#

and that in turn might give us some further clues

#

like

#
                            this.hasSendStartMeasure = true;
                            mo44244d(5);
                        }```
onyx zenith
#
2023-08-22 16:41:53,671 __main__ INFO: BleVersion: : 56
2023-08-22 16:41:53,671 __main__ INFO: weightRatio: : 100.0```
#

Got some info back...

#

Does line up with this version:
[Characteristic] 00002a26-0000-1000-8000-00805f9b34fb (Handle: 13): Firmware Revision String (read), Value: bytearray(b'V56.0')