Add Relaxmedic scale support (icomon protocol 107) - #1441
Open
Caiofiga wants to merge 1 commit into
Open
Conversation
Shares service 0xFFB0 and the 20-byte frame family with RobiS9Handler and RunstarR6Handler, but differs in two ways. Frames fragment: byte 2 is a fragment index, not a constant zero, and the 0xA3 result spans two frames. Every fragment contributes bytes[3..18], so the byte after the fragment index is payload rather than a header byte to skip. Both existing handlers in this family reject such frames outright. The 0xBA profile is generated rather than replayed. RobiS9Handler replays a captured handshake because its timestamp and token could not be regenerated; this one is understood field by field, so it is built from the openScale user. flags1 carries sex and age and is the only place they are sent -- getting it wrong is silent, the scale simply computes for the wrong person. The generated frame is byte-identical to a capture the scale accepts. Body composition comes from Wla25BodyComposition, ported from ICBodyFatAlgorithmWLA25::calc in the vendor library: fat mass is a 13-term linear regression over height, weight, the rounded BMI and all ten impedances, and the rest follows from fat-free mass. Verified against the vendor library over 240 randomised inputs spanning both clamp boundaries, both impedance branches and both sexes -- exact on all ten fields. Three roundings are load-bearing and each is worth a tenth of a unit: the weight is rounded before anything else, the BMI fed into the regression is rounded, and fat mass is rounded before fat-free mass is derived from it. The rounding is half-up and computed in float32; everything else runs in double, matching the library. Doing it all in Float shifts BMR by 1 kcal in roughly one case in eighty. compute() takes no sex parameter: this algorithm's body composition does not depend on it, only metabolic age does. Known limitation: bone mass can read 0.1 kg below the scale's own display. The vendor app agrees with this code, and no algorithm the vendor library ships reproduces the scale's combination of values, so the scale's firmware appears to compute bone differently. Metabolic age is implemented but not published -- ScaleMeasurement has no field for it. EtekcityLib and HesleyHandler hit the same wall, one computing it and the other reading it off the wire, both discarding it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Adds a Handler for the Relaxmedic FG2015B-A scale, reverse-engineered by decompiling the vendor app, plus testing on the physical device.
Working: Weight; Body fat; Water; Muscle; Bone; Visceral fat; Protein; BMR; LBM.
Not Implemented: Offline history (Not supported);Segmented values.
It shares service
0xFFB0and the 20-byte frame family withRobiS9HandlerandRunstarR6Handler, but has fragmented frames, where byte 2 works as an index, with the result spanning 2 frames.Attached is the btsnoop capture, filtered to this scale's traffic only.