Fix: body composition computed for the wrong user profile#1411
Open
DanyPM wants to merge 1 commit into
Open
Conversation
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.
Fix: body composition computed for the wrong user profile
Problem
When you weigh in while a different user profile (B) is active on the app (or the scale auto-routes the reading by weight), the measurement is correctly stored under the right user (A) — but its body-composition values were computed from the wrong user's (B) biodata (sex, height, age).
Because BIA (bioelectrical impedance) results are driven by sex and height, the error can be large. Concrete case from a backup: a male user (172 cm) showed 30.1 % body fat. Re-deriving that row with the other profile on the device (female / 160 cm / age 31) reproduces every stored field exactly (fat 30.08, water 51.18, muscle 35.20, ecw 23.62, icw 27.56, bmr 1471, vfi 8.02); re-deriving with his real profile (male / 172 cm / 28) gives the correct ~17.8 %. The formula is fine — it was fed the wrong user.
Root cause — body composition is computed before the user is finalized
The user a measurement belongs to is decided in three places, but body composition is calculated up front against the wrong one and never recomputed:
BleConnector.saveMeasurementFromEventdiscards that and rebuilds the row with the current app user.MeasurementTransformationUseCase.applySmartUserAssignmentreassigns the row to the closest-weight user.So the persisted body composition belongs to a different profile than the row it lands on. This affects every BIA scale, not just one model, and recurs on every weigh-in until the reading is re-derived for the final user.
Fix
1. Live fix — compute with the final user (all BIA scales)
Body composition is now derived after the user is finalized, then computed once for that user:
resolveAssignedUserId(weightKg, fallbackUserId)fromapplySmartUserAssignment(pure decision, no mutation).ScaleDeviceHandler.recomputeBodyComposition(raw, user)and threaded it throughScaleCommunicator→ModernScaleAdaptersoBleConnectorcan re-derive for the assigned user via a recompute lambda passed toMeasurementFacade.saveMeasurementFromBleDevice.core/bluetooth/libs/BodyComposition.kt(fromMiScale,fromTrisa,standardImpedanceLib) — so each calculator lib is bound to(user, raw)once. Same-lib handlers reduce to one-liners.Handlers fixed (13): S400, MiScale, EufyC20, OneByone, OneByoneNew, Trisa, Etekcity, Cult, QN, HuaweiCH100S, DrTrust (VFI excepted — needs segmental z3 that isn't persisted), ES-CS20M, Realme.
Not fixable: Yunmai, Soehnle never persist impedance → raw input is gone (default no-op). HuaweiAhCh100 sends a final fat %, which is user-independent (no override needed).
2. Retroactive fix —
MIGRATION_15_16Re-derives body composition for every stored dual-band S400 row using each row's own user via
S400BodyComposition.compute(S400 defaults: MI_LEGACY bone, Cunningham-1991 BMR, foot-to-foot 1.10). Rows whose inputs fall outside the validated range (NOT_AVAILABLE) are left untouched. Raw weight/impedance are never modified.The migration is S400-only by necessity: it is the only scale that persists enough raw input and has a centralised pure formula to reconstruct from. The
Measurementtable stores no device id, so an arbitrary historic row can't be matched to the right per-scale formula.Tests
MigrationTest.migration15to16_reDerivesS400BodyCompositionForRowUser— seeds a male row carrying female-derived body fat + raw impedance; asserts the migration rewrites it to the male re-derivation (~17.8 %), leaves weight/impedance untouched, and skips an out-of-range row.BackupRestoreUseCasesTest) are pre-existing Windows file-swap flakes — identical on a cleanmastercheckout.Notes / follow-ups
DrTrustVFI andYunmai/Soehnleare documented limitations above.Migration); this affects only BONE/BMR magnitude, never fat/water/muscle.