Skip to content

feat: Add support for Afu B1 - #1485

Open
icealtria wants to merge 1 commit into
oliexdev:masterfrom
icealtria:afu-b1
Open

feat: Add support for Afu B1#1485
icealtria wants to merge 1 commit into
oliexdev:masterfrom
icealtria:afu-b1

Conversation

@icealtria

Copy link
Copy Markdown

AFU B1 body fat scale (an AFU × Boohee Health co-brand) — shows up on Bluetooth as AFU-BH-TZ-B1. But I guess this is a scale made by ICOMON.

Summary

This PR adds a dedicated AfuB1Handler plus its library AfuB1Lib.

What works: weighing, history import, time sync, user profile sync, and body composition derived.

Everything was reverse-engineered from HCI snoop captures of the official app and Boohee app.

Protocol (reverse-engineered)

Every frame is exactly 20 bytes, in both directions:

[AF][45][cmd @2][body @3..17][wire type @18][crc @19]    crc = sum(raw[2..18]) & 0x1F

Wire types: 0x51 live weight, 0x54 stored/history record, 0x52 single impedance result, 0x5f control (sub-cmds 0x12 ACK, 0x14 chunk request / report chunk, 0x16 time-sync), 0x61 user profile and 0x62 sync-request (phone → scale only).

The session flow that mirrors the official app:

  1. Handshake — time-sync + 2× sync-request.
  2. Idle history — while idle, the scale keeps pushing its stored measurement (0x54) until it gets an ACK. A history-dump request (0x8e) then makes it send every stored record once.
  3. Weigh-in — live weight streams until the scale marks the reading as final (a state bit inside the frame); the phone ACKs and waits for the scale to go idle (user stepped off). Re-sync + getWeighAgain profiles (0x4b/0x5b) then make the scale push the freshly committed 0x54 record.
  • The history queue must be drained before a weigh can be pulled — otherwise the commit flow returns the old queued record instead of the fresh measurement.

The full annotated frame layout lives in the AfuB1Lib header comment.

Body composition

The scale itself only ever sends weight + raw impedance. The official app computes body fat in the cloud, therefore, I cannot obtain the actual algorithm.

  • Body-fat % comes from a formula ported from the Boohee app's algorithm library. Its constants fitted to the official app's results.
  • Water %, skeletal muscle %, bone mass and visceral fat index are derived from that body-fat value with formulas also reverse-engineered from the Boohee app. With identical inputs they match the official app exactly.

Testing

  • All AfuB1 unit tests pass: /gradlew test.
  • Tested on a physical device.

AI disclaimer

I used generative AI to help write this solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant