Skip to content

Add support for PICOOC Mini Lite (PICOOC-L broadcast scale) - #1512

Merged
oliexdev merged 2 commits into
oliexdev:masterfrom
dnsfdv:feature/picooc-broadcast-mini-lite
Sep 6, 2026
Merged

Add support for PICOOC Mini Lite (PICOOC-L broadcast scale)#1512
oliexdev merged 2 commits into
oliexdev:masterfrom
dnsfdv:feature/picooc-broadcast-mini-lite

Conversation

@dnsfdv

@dnsfdv dnsfdv commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Closes #1511.

Model: PICOOC Mini Lite, advertises as PICOOC-L — a non-connectable broadcast scale (weight and impedance inside BLE manufacturer-specific data), so the handler uses broadcast-only mode with no pairing, like the AiLink family.

What works

  • Live weight and impedance from broadcast advertisements (final frame is state 0x39; in-progress frames re-arm the completion guard)
  • Body composition: clean-room port of the vendor app's 4.3.0 White/Caucasian BIA path — body fat, skeletal muscle, water, bone mass, protein, visceral fat, plus the vendor-specific total muscle and metabolic age (custom measurement types) and vendor BMR
  • Online anchor learning reproducing the vendor's anchor_weight/anchor_bata profile calibration: a beta cluster is promoted after four nearby BIA measurements and persisted per user; calibration resets when profile inputs change
  • Composition is published only for impedance >= 50 ohm and suppressed while assisted weighing is active (pet/infant weighing)

Design notes (per the wiki ground rules)

  • One library file, PicoocWhiteBodyComposition.kt (BIA math + anchor learner), free of Android dependencies and unit-tested on the JVM; the handler stays a thin byte-moving layer
  • Only fields actually observed in captures are decoded; unknown fields are ignored
  • Exact PICOOC-L name match, registered in ScaleFactory immediately before ScaleupHandler's generic 0xD0 manufacturer match (with a comment); catalog fixture + remark added
  • Vendor-computed metrics (BMR, metabolic age, total muscle) are published under the vendor's semantics so users migrating from the vendor app keep comparable history — happy to drop any of them in favour of the app's derived-value system if preferred
  • New user-facing strings are in values/strings.xml only; translations to follow via Weblate

Shared-infrastructure touch (please double-check)

Two lines plumb the existing useAssistedWeighing flag (introduced for UI/database in 9436dcf) through ScaleUser into the Bluetooth layer, so the handler can suppress body composition while assisted weighing is active. No other handler reads or is affected by the field; happy to split this into a separate PR if preferred.

Testing

  • Unit tests replay advertisements captured from a physical Mini Lite (byte layout documented in the parser) plus BIA regression vectors; ./gradlew test is green
  • Verified on-device against the vendor app's readings
  • No HCI snoop log: the scale is broadcast-only, so representative payloads are embedded in the regression tests instead

dnsfdv and others added 2 commits September 5, 2026 16:29
Non-connectable PICOOC-L advertisements carry weight and impedance; body
composition is a clean-room port of the vendor's 4.3.0 White/Caucasian BIA
path, with an online learner for the vendor's anchor_weight/anchor_beta
profile state.

Plumbs the existing useAssistedWeighing flag (9436dcf) into ScaleUser so
the handler can skip composition while assisted weighing is active.
@oliexdev

oliexdev commented Sep 5, 2026

Copy link
Copy Markdown
Owner

I reverted the useAssistedWeighing plumbing: applyAssistedWeighing returns the
weight difference alone, so every other value is dropped before the save — the
driver-side suppression couldn't change anything. ScaleUser and
ModernScaleAdapter are untouched now.

I made the two new types generic (ble.total_muscle, ble.metabolic_age) — a
vendor-bound path breaks the history when a user switches scales. That turned out to
unblock seven other handlers that had a metabolic age commented out for want of a
type; they all publish it now on your identity.

Smaller things: dropped the BMI write (DerivedValuesCalculator overwrites it),
dropped the manual anchor/beta fields (the four-measurement learning covers it), and
made PicoocAnchorLearner.accept skip instead of throw — it runs inside the
advertisement callback, where that costs the whole measurement.

Two questions, no code needed:

  1. Does the scale repeat the final 0x39 frame while idle? If so, the next session's
    first advertisement would land as a fresh measurement with the current timestamp.
  2. Only the Caucasian/White path is ported and it applies to every user. It's in the
    catalog remark, but I'd add a sentence where users can see it.

@dnsfdv

dnsfdv commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Yes, 0x39 repeats. The attached log contains two weighings, with the scale going to sleep between them. The second weighing included extra weight: 82.0 → 86.3
kg, impedance 504 → 505 Ω. Each result repeats unchanged for about 7 seconds. All captured packets are 0x39; no intermediate states appear.

Restarting scanning during that burst could accept the same result again. In another capture, two separate weighings produced identical payloads, so comparing
payloads alone would also reject valid measurements.

The 0x39 check comes from the vendor app’s isTrueWeightData(). The 0x38 test packet is synthetic; treating other states as “in progress” was my unverified
assumption.

Agreed on adding the White/Caucasian model limitation to the device settings.

One concern about useAssistedWeighing: composition values are discarded before saving, but the handler has already persisted anchor and learner state. For a
selected assisted-weighing profile aged 16+ with a valid height, the combined weight could affect calibration. The age check already excludes infant profiles.
D0_49_00_4B_4F_1E - 2026-09-05 19_45_07.txt

@oliexdev
oliexdev merged commit fa8f768 into oliexdev:master Sep 6, 2026
@oliexdev

oliexdev commented Sep 6, 2026

Copy link
Copy Markdown
Owner

thanks for your PR 👍

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.

Support PICOOC Mini Lite scale (PICOOC-L broadcast protocol)

2 participants