#worth looking at https github com hbldh
1 messages · Page 1 of 1 (latest)
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```
It loooks like discord has swallowed the formatting
Better now?
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
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)```
Lovely
There are 3 other sections:
Generic Access Profile
Device Information
Battery Service
[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')```
If there’s anything “read”, does it correlate with a value you know
\x00 is a weird battery level
All values seem to be that Byte Array
No, batteries are relatively new
Anyway it’s probably worth trying the notify example with characteristic handle 18
But also the Vendor specific section also shows the same \x00
So I would use enable_notifications.py for that now?
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
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```
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
At this point I’d either try decompiling one of the android apps (sorry been a while I’d be no help there) or trying to wireshark them (https://wiki.wireshark.org/Bluetooth, https://hackaday.com/2021/03/23/a-crash-course-on-sniffing-bluetooth-low-energy/ etc)
Also googling uuids can be helpful
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"));
}
}```
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...
Can you find any code that uses them?
Do any of those match what we found with service browser?
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);
}```
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?
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);
}```
first log message: Send the Set Weight Unit command
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
second log message: Update the user's latest measurement history
Aaah that is cool!
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?
No not really, some UI references only
scaleWspBleManager15.userDefinedWrite and scaleWspBleManager14.userDefinedRead?
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
Yeah you probably right
so anyhwere it does ScaleWspBleManager scaleWspBleManager??? = ScaleWspBleManager.this;
might then have a scaleWspBleManager???.userDefinedWrite / userDefinedRead
so you are probably better search for userDefinedWrite/userDefinedRead
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)```
Did some googling and found these two repos:
https://github.com/YolandaQingniu/qnscalesdk
https://github.com/YolandaQingniu/SDK-Band-Android
Which gets referenced often
Could I not perhaps send you the decompiled code?
For a quick search from your side?
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\
ah
so the commented out stuff was commented with this, right?
/* Code decompiled incorrectly, please refer to instructions dump. */
Ah yeah saw those - Not sure how else to decompile it...
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
Yeah I think there are multiple Apps under this same Code Base
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;
}```
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
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
That is the Indicate characteristic right
so i think it would write an empty string to the write characteristic
which baffles me
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
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
Yeah so perhaps the Scale needs an ACK in order to show the Bluetooth icon
it looks like the "meat" of understanding the notify callback is in QNDecoderImpl.java
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));
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
Okay I see what you mean
So could I perhaps use Bleak to write to that UUID?
What would happen?
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
Okay, how would I go about getting the debug logs of the App?
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
writeWeightUnit seems likely
i was looking at readWeightUnit
Ah yeah probably that
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
Precision seems to be 0.05 KG
yeah no way thats a byte
Max Weight is also 150KG
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"
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
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
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))```
Not too sure if that checksum is correct.
But I get the printed:
[3, 1, 0, 4]
write: None
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')```
did you start the notify before writing?
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)```
did the device do anything different?
Seems exactly the same...
bytearray(b'\x14\x01\x00\x15')
Think this is not correct
This is what I get as my Bytarray
Same results: None
I've just hardcoded the bytearray for now
im expecting None tbh
im epxecting the result to come from the notification handler
or for no response at all
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')
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
But from the method here, they are simply doing this:
QNLogUtils.logAndWrite(TAG, "发送读取体重单位命令:" + ConvertUtils.bytesToHexStr(ConvertUtils.listToBytes(arrayList)));
ConvertUtils.bytesToHexStr
right
but they aren't dealing with any response here are they
they just spaff some bytes into a characteristic and run away
Aaah yeah makes sense
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
I see
isSupportSt looks like it would be false
((bArr2[10] >> 1) & 1) == 1 || ((bArr2[10] >> 2) & 1) == 1
So I kinda need to write that over to a Python script then to decode right
exactly
and that in turn might give us some further clues
like
this.hasSendStartMeasure = true;
mo44244d(5);
}```
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')