I got step tracking working! It wasn't too hard once I understood that the android app packet parser had state that was kept between packets depending on if it returns true or false. ```python def bcd_to_decimal(b: int) -> int: return (((b >> 4) & 15) * 10) + (b & 15) @dataclass class SportDetail(): year: int month: int day: int time_index: int calories: int steps: int distance: int class SportDetailParser(): r""" Parse SportDetailPacket, of which there will be several example data: bytearray(b'C\xf0\x05\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x009') bytearray(b'C#\x08\x13\x10\x00\x05\xc8\x000\x00\x1b\x00\x00\x00\xa9') bytearray(b'C#\x08\x13\x14\x01\x05\xb6\x18\xaa\x04i\x03\x00\x00\x83') bytearray(b'C#\x08\x13\x18\x02\x058\x04\xe1\x00\x95\x00\x00\x00R') bytearray(b'C#\x08\x13\x1c\x03\x05\x05\x02l\x00H\x00\x00\x00`') bytearray(b'C#\x08\x13L\x04\x05\xef\x01c\x00D\x00\x00\x00m') """ def __init__(self): self.reset() def reset(self): self.new_calorie_protocol = False self.index = 0 self.details: list[SportDetail] = [] def parse(self, packet: bytearray): assert len(packet) == 16 assert packet[0] == CMD_GET_STEP_SOMEDAY if self.index == 0 and packet[1] == 255: self.reset() return if self.index == 0 and packet[1] == 240: if packet[3] == 1: self.new_calorie_protocol = True self.index += 1 else: year = bcd_to_decimal(packet[1]) + 2000 month = bcd_to_decimal(packet[2]) day = bcd_to_decimal(packet[3]) time_index = packet[4] calories = packet[7] | (packet[8] << 8) if self.new_calorie_protocol: calories *= 10 steps = packet[9] | (packet[10] << 8) distance = packet[11] | (packet[12] << 8) details = SportDetail( year=year, month=month, day=day, time_index=time_index, calories=calories, steps=steps, distance=distance ) print("Details: ", details) self.details.append(details) self.index += 1 if packet[5] == packet[6] - 1: self.reset() ``` This is what I get ``` Details: SportDetail(year=2023, month=8, day=13, time_index=16, calories=2000, steps=48, distance=27) Details: SportDetail(year=2023, month=8, day=13, time_index=20, calories=63260, steps=1194, distance=873) Details: SportDetail(year=2023, month=8, day=13, time_index=24, calories=10800, steps=225, distance=149) Details: SportDetail(year=2023, month=8, day=13, time_index=28, calories=5170, steps=108, distance=72) Details: SportDetail(year=2023, month=8, day=13, time_index=76, calories=6520, steps=126, distance=90) ``` Calories seem pretty high and the time_index is a bit mysterious. Pretty sure this is across multiple days Anyway, I want to set the time so let's look at the `SetTimeReq` ```java public SetTimeReq(int i) { super((byte) 1); this.mLanguage = (byte) 0; this.mData = new byte[7]; this.mLocaleMap = new HashMap(); initMap(); setLanguage(); Calendar calendar = Calendar.getInstance(); calendar.add(13, i); this.mData[0] = BLEDataFormatUtils.decimalToBCD(calendar.get(1) % 2000); this.mData[1] = BLEDataFormatUtils.decimalToBCD(calendar.get(2) + 1); this.mData[2] = BLEDataFormatUtils.decimalToBCD(calendar.get(5)); this.mData[3] = BLEDataFormatUtils.decimalToBCD(calendar.get(11)); this.mData[4] = BLEDataFormatUtils.decimalToBCD(calendar.get(12)); this.mData[5] = BLEDataFormatUtils.decimalToBCD(calendar.get(13)); } ``` Using `jshell` (which I have installed somehow?) to figure out the exact fields. `1` is the year. `2` is the 0-indexed month. `5` is the day. `11` is the hour. `12` is the minute. `13` is the second. I guess we add an offset of either 0 or 1 for some reason. Looks like the language is also set? ```java private void initMap() { this.mLocaleMap.put("zh_CN", 0); this.mLocaleMap.put("en", 1); this.mLocaleMap.put("zh_HK", 2); this.mLocaleMap.put("zh_TW", 2); this.mLocaleMap.put("el", 3); this.mLocaleMap.put(Localization.language, 4); this.mLocaleMap.put("de", 5); this.mLocaleMap.put("it", 6); this.mLocaleMap.put("es", 7); this.mLocaleMap.put("nl", 8); this.mLocaleMap.put("pt", 9); this.mLocaleMap.put("ru", 10); this.mLocaleMap.put("tr", 11); this.mLocaleMap.put("ja", 12); this.mLocaleMap.put("ko", 13); this.mLocaleMap.put("pl", 14); this.mLocaleMap.put("ro", 15); this.mLocaleMap.put("ar", 16); this.mLocaleMap.put("th", 17); this.mLocaleMap.put("vi", 18); this.mLocaleMap.put("in", 19); this.mLocaleMap.put("hi", 20); this.mLocaleMap.put("cs", 21); this.mLocaleMap.put("sk", 22); this.mLocaleMap.put("hu", 23); this.mLocaleMap.put("iw", 24); this.mLocaleMap.put("hr", 25); this.mLocaleMap.put("sl", 26); this.mLocaleMap.put("ur", 35); } @Override // com.oudmon.ble.base.communication.req.BaseReqCmd protected byte[] getSubData() { byte[] bArr = this.mData; bArr[6] = this.mLanguage; return bArr; } public void setLanguage() { String language = Locale.getDefault().getLanguage(); if (language.startsWith("zh")) { language = Locale.getDefault().getLanguage() + "_" + Locale.getDefault().getCountry().toUpperCase(); } Integer num = this.mLocaleMap.get(language); int intValue = num == null ? 1 : num.intValue(); AwLog.i(Author.HeZhiYuan, "SetTimeReq -> mLanguage: " + language + ", value: " + num + ", result: " + intValue); this.mLanguage = (byte) intValue; } ``` So the first 5 bytes are BCD encoded date/time and the 6th byte is the language based on this mapping. Here's the python equivalent ```python CMD_SET_TIME = 1 def byte_to_bcd(b: int) -> int: assert b < 99 assert b > 0 tens = b // 10 ones = b % 10 return (tens << 4) | ones def set_time_packet(target: datetime | None = None) -> bytearray: if target is None: target = datetime.now() data = bytearray(7) data[0] = byte_to_bcd(target.year % 2000) data[1] = byte_to_bcd(target.month) data[2] = byte_to_bcd(target.day) data[3] = byte_to_bcd(target.hour) data[4] = byte_to_bcd(target.minute) data[5] = byte_to_bcd(target.second) data[6] = 1 # set language to english, 0 is chinese return make_packet(CMD_SET_TIME, data) ``` This seems to have worked, the next day when I ask for sport details, the year, month and date are correct. (well, the month was off by 1 because I didn't read the docs) This does seem to return an error though ``` bytearray(b'/\xf1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 ') bytearray(b'\x01\x00\x01\x00"\x00\x00\x00\x00\x01\x000\x01\x00\x10f') ``` The initial `\xf1` byte in the packet has the high bit set which indicates an error