Skip to content

Feat/terraillon smart connect web coach 14754 - #1568

Open
sebelectron wants to merge 8 commits into
oliexdev:masterfrom
sebelectron:feat/terraillon-smart-connect-web-coach-14754
Open

sebelectron wants to merge 8 commits into
oliexdev:masterfrom
sebelectron:feat/terraillon-smart-connect-web-coach-14754

Conversation

@sebelectron

Copy link
Copy Markdown

Terraillon Smart Connect (ref. 14754)

Bluetooth body-composition scale sold by Terraillon (vendor apps: Wellness Coach, now MyHealth). It is built on a Transtek A3 module: manufacturer "Transtek", model "380", firmware "A3". It advertises a 14-digit name starting with 01376 and service 0x7892.

Protocol

Same Transtek A3 family as WeightGurusA3Handler / BodyConnectHandler (0x8A24 weight FLOAT frames, 0x8A22 SFLOAT body-composition frames, 0x8A81/0x8A82 command channel), with these differences, all taken from BTSnoop captures of MyHealth:

  • Service 0x7892.
  • Challenge: the scale sends a1 <random>, and the host answers 20 <password xor random>. The password is the low four bytes of the scale's MAC address, reversed. A password handed out via 0xA0 takes precedence.
  • Session as done by MyHealth: challenge reply, 02 <local time since 2010>, then 22 after the c0 echo. The scale then uploads every stored weigh-in. No profile (0x51) is written; body composition is computed on the scale from the profile stored by the vendor app.
  • Bone mass arrives in kg (Weight Gurus sends a percentage).

Every decoded field (weight, impedance, BMR, fat, water, visceral fat, muscle, bone) was checked against the values MyHealth displayed for the same weigh-ins.

Files

  • scales/TerraillonWebCoachHandler.kt: GATT session only.
  • libs/TerraillonLib.kt: frame parsing and the challenge reply, no Android dependencies.
  • libs/TerraillonLibTest.kt: frames with the captured layout, but an invented reading and MAC.
  • ScaleFactory.kt / ScaleCatalog.kt: registration and fixture.

./gradlew testDebugUnitTest and assembleDebug pass on CI.

Real-device test

  • Only tested with my device

Limitations

  • Tested with a single unit. The MAC-derived password is inferred from that one scale and may not hold for every unit.

Password derivation (example)

  • Other Terraillon Web Coach models may use other model codes or services and may not be detected.
  • The challenge password is not handed out at pairing: it is the low four bytes of the scale's MAC address, in over-the-air (little-endian) order.

Example with an invented address C0:FF:EE:12:34:56:

  • last four bytes: EE 12 34 56
  • reversed, which gives the password: 56 34 12 EE
  • the scale sends the random a1 11 11 11 11
  • the host replies 20 + byte-wise XOR: 56^11 34^11 12^11 EE^11 = 47 25 03 FF, so the frame is 20 47 25 03 ff

On the real unit, the reply MyHealth sent matched this rule on all four bytes, so a coincidence is ruled out. What I could not determine from a single scale is whether every unit follows it, or whether the password was set to the MAC during the first pairing with the vendor app. If another owner's scale rejects the reply, the handler still accepts a password sent via 0xA0 in pairing mode. That path is untested, though.

Captures are available on request (MAC and personal data scrubbed).
Feel free to squash on merge.

@oliexdev

Copy link
Copy Markdown
Owner

Please add it to WeightGurusA3Handler instead of a new handler. It's the same Transtek A3
protocol: same characteristics and opcodes, and the 0x8A24/0x8A22 parsing, flushPendingWeight,
publishIfNew and the 2010-based time conversion are already there. Pick a model in supportFor()
(^[01]1376\d+$ → Terraillon) and branch only where the scale really differs:

  • service 0x7892 instead of 0d005750-…,
  • password: the stored 0xA0 one, otherwise the reversed low four MAC bytes,
  • session: 0xA1 → 0x20 + 0x02, then 0x22 on the 0xC0 echo (with your fallback), no pairing flow,
  • bone mass in kg instead of a percentage.

TerraillonLib can go then: floatFrom32, sfloatFrom16, xor and hexToBytes already exist in
the handler's companion. Please put the tests into WeightGurusA3HandlerTest.

Also:

  • Drop the byService fallback in supportFor(). It claims any name of 8+ digits on 0x7892, while
    only 01376… has been seen, and NAME_PATTERN already covers that.
  • Body composition comes from the profile MyHealth stored on the scale, so a changed height or age
    in openScale doesn't reach it. The Weight Gurus path writes the profile with 0x51 in every session.
    If you get the chance, please try whether the Terraillon accepts that too.
  • If you get the chance, one scrubbed 0x8A24 + 0x8A22 pair from a capture, with the values MyHealth
    showed for it, would make the tests check real data.

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.

2 participants