Huawei AH100/CH100: fix the user record sent to the scale - #1450
Open
martinriedel wants to merge 4 commits into
Open
Huawei AH100/CH100: fix the user record sent to the scale#1450martinriedel wants to merge 4 commits into
martinriedel wants to merge 4 commits into
Conversation
A Huawei CH100 capture showed the scale answering our USER_INFO with USER_CHANGED 127 times in 33 seconds, re-requesting the record after every reply. The record we sent carried weight 0: sendUserInfo falls back to user.initialWeight when no measurement has been taken yet in the session, and that field is 0 for a freshly created profile. Add ScaleDeviceHandler.fallbackWeightKg(), which walks last stored measurement -> profile initial weight -> BMI-22 estimate from body height, and never returns 0. On the same hardware this drops the USER_CHANGED storm from 127 cycles to 5. HuaweiCH100SHandler had the identical line and is fixed with it. Also pin two things the previous fix got wrong on paper: the encrypted USER_INFO payload is 16 bytes, not 14, and the unit tests do not remove the need to verify against real hardware. Most fixtures in HuaweiAhCh100HandlerTest were generated by a second implementation of the same assumed layout, so they can only show that two ports of one guess agree. Add a section with frames captured off a real scale and checked against its display, including the 16th byte the documented layout does not mention. Not addressed: the body fat percentage. It arrives fully computed in the frame, and re-measuring with a correct user record moved it from 33.8% to 33.9% -- i.e. not at all. The scale's own algorithm produces it, and at the reported 313 ohm StandardImpedanceLib explicitly declines to do better. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The two captures behind that claim were 0.1 kg apart, so a slowly-varying value like visceral fat level would look constant either way. oliexdev#547 reports visceral fat 11.5 at BMI 24.8 and 14 at BMI 27.5 from the vendor app; these captures are BMI 37 and the byte reads 21. Pin the value, do not explain it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The vendor app sends "DC 0E 09" followed by 16 encrypted bytes: the trailing 2-byte constant is transmitted but sits outside the declared length. v2.5.4's "Total = 14 bytes" note describes the same thing; the 3.x port pulled the trailer into the payload and moved the length byte to 16 with it. Measured on real hardware. Two idle sessions, connected, nobody standing on the scale, so the only variable is the length byte: 0x10 (16) 1026 USER_CHANGED polls over 274 s median 266 ms 0x0E (14) 533 USER_CHANGED polls over 532 s median 1001 ms The scale polls four times slower once the record is declared the way its own app declares it. Note what this does NOT show: the same comparison across the weight fix in the previous commit finds no difference (245 ms with weight 0 vs 266 ms with a correct weight, both at 0x10). That fix stands on sending correct data, not on this behaviour -- see the correction in the PR description. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Open
4 tasks
Owner
|
Thanks for your PR 👍 I've pushed one change to your branch: the fallbackWeightKg / profileWeightKg pair is out of ScaleDeviceHandler again, and the logic now sits as a private profileWeightKg(user) Could you re-run this against your CH100 before I merge? The user record path is exactly what changed, and I'd rather have it confirmed on the hardware than reason about it. |
Author
|
Will check and reply |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1449.
Two independent defects in the
USER_INFOrecord this handler pushes to thescale. Both verified on real hardware (advert name
CH100, Chipsea CST34M97).1. Never send a zero weight
sendUserInfofalls back touser.initialWeightwhen no measurement has beentaken yet in the session, and that field is
0fon a fresh profile. Decryptedfrom the wire:
ScaleDeviceHandler.fallbackWeightKg()walks last stored measurement → profileinitial weight → BMI-22 estimate from body height, and never returns 0.
HuaweiCH100SHandlerhad the identical line and is fixed with it.USER_CHANGED cycles: 127 → 5.No measurable effect on scale behaviour: inmatched idle sessions the poll rate is 245 ms with weight 0 and 266 ms with a
correct weight. This is a data-correctness fix, not a behaviour fix.
2. Declare 14, not 16, in the length byte
Huawei's own app sends
DC 0E 09followed by 16 encrypted bytes — the trailing2-byte constant is outside the declared length. v2.5.4's "Total = 14 bytes" note
says the same; the 3.x port pulled the trailer into the payload and shifted the
length byte with it.
Two idle sessions, connected, nobody standing on the scale, length byte the only
variable:
0x10(16)0x0E(14)Four times slower once the record is declared the way the vendor declares it.
Not in scope: the body fat value
Worth stating explicitly, given this handler's history of "wrong values" reports.
The measured values are correct — weight, body fat, impedance, timestamp and
weekday all decode exactly as the scale's display shows them. This is not
#1206 / #1280 resurfacing.
Body fat arrives fully computed in the frame and does not respond to anything we
send (33.8% → 33.9% across the user-record fix).
StandardImpedanceLibcannot dobetter: against Huawei's own numbers it reads 10–14 points low on every sample I
have, including the vendor-app captures in #547.
Tests
Most fixtures in
HuaweiAhCh100HandlerTestwere generated by a secondimplementation of the same assumed layout, so they can only show that two ports
of one guess agree — they would stay green if the hardware disagreed. Added a
Real hardware capturesection with frames taken off a scale and checked againstits display.
That section also pins the 16th byte the documented layout does not mention.
Decoding the btsnoop captures attached to #547 gives 13 measurements of one AH100
over 11 days: the byte is
0x4Eon every one while weight (87.3–93.2 kg), bodyfat (22.9–25.3 %) and impedance (373–451 Ω) all move. A per-device constant, so
dropping it is correct.
Those same captures answer a question that comes up periodically: AH100 and
CH100 do not need separate handlers. A 2020 AH100 driven by the Huawei app uses
the same frame start bytes, the same opcodes, the same field order and the same
scaling as a 2026 CH100 driven by openScale. Splitting on the advert name would
also split on the wrong key — AH100 hardware advertises as
CH100(see #1313 andmy own unit).
Full unit test suite passes.
🤖 Generated with Claude Code