Skip to content

MiScaleHandler: derive the v1/v2 variant from the GATT table instead of trusting the scan-time name heuristic - #1490

Open
l9i wants to merge 2 commits into
oliexdev:masterfrom
l9i:miscale-variant-detection
Open

MiScaleHandler: derive the v1/v2 variant from the GATT table instead of trusting the scan-time name heuristic#1490
l9i wants to merge 2 commits into
oliexdev:masterfrom
l9i:miscale-variant-detection

Conversation

@l9i

@l9i l9i commented Aug 26, 2026

Copy link
Copy Markdown

The weight-only Mi Smart Scale 2 (XMTZC04HM) advertises "MI SCALE2" and even carries the 0x1530 vendor service, so the scan-time heuristic classifies it as v2. Its GATT table, however, only serves the Weight Scale service (0x181D) — there is no Body Composition service (0x181B). The v2 init sequence then subscribes/writes against a non-existent service and the app hangs forever in "waiting for measurement".

Changes:

  • Connect-time variant detection. After service discovery the variant is derived from the actual GATT table, probing for the Mi history characteristic (vendor 0x2A2F) under 0x181B (→ v2) and 0x181D (→ v1). This corrects the misclassified XMTZC04HM to v1 and it inits against 0x181D.
  • Clones keep working. Devices that omit the vendor history characteristic but still serve a standard service fall back to service-presence detection (their live weight arrives via the standard 0x2A9D characteristic). Only a device exposing neither 0x181B nor 0x181D aborts with a user-visible error and a disconnect, instead of hanging.
  • supportFor() is now side-effect-free. The handler is a shared singleton and the UI re-queries device support while a session is live; previously such a query could reset the GATT-corrected variant mid-session and silently drop live v1 frames.
  • Detected variant is remembered. The GATT-detected variant is persisted per device address and consulted by scan-time support queries, so a downgraded device stops advertising v2-only capabilities (body composition, unit config) it cannot deliver.
  • Tests. MiScaleHandlerVariantTest covers scan-time classification, the XMTZC04HM downgrade, genuine v2, the clone fallback (a live 10-byte weight frame is parsed end-to-end), a mid-session supportFor() query not clobbering the session variant, the persisted-variant override, and the neither-service abort. Timer-dependent tests run on runTest virtual time instead of racing the real 1-second history-fallback timer.
    65 test classes • 544 tests • 0 failures • 0 errors • 0 skipped
      └ MiScaleHandlerVariantTest: 7 tests, all passed
    

Fixes #1489.

l9i and others added 2 commits August 26, 2026 09:02
Re-detect the Mi scale variant on connect by probing for the history
characteristic under 0x181B (V2) / 0x181D (V1), correcting the ambiguous
scan-time name heuristic. The weight-only Mi Smart Scale 2 (XMTZC04HM)
advertises "MI SCALE2" and carries the 0x1530 vendor service but serves
only 0x181D, so V2 mode subscribed/wrote against a non-existent service
and hung in "waiting for measurement" forever.

Fail fast with a user-visible error + disconnect when the history
characteristic exists under neither service. Adds MiScaleHandlerVariantTest.

Fixes oliexdev#1489

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Follow-up to e77aa89, fixing the issues found in review:

- supportFor() no longer mutates the session variant field. The handler
  is a shared singleton and the UI re-queries device support at any time
  (savedDeviceSupport, scan results), which could silently reset a
  GATT-corrected variant mid-session and drop live v1 frames.
- detectVariantFromGatt() no longer hard-rejects clones that serve a
  standard service without the vendor history characteristic (live
  weight via 0x2A9D worked for them before the fail-fast). Service
  presence acts as a fallback tier; only a device exposing neither
  0x181B nor 0x181D aborts, which is also exactly what the user-facing
  error string claims.
- The GATT-detected variant is persisted per device address so later
  scan-time support queries stop promising v2 capabilities (body
  composition, unit config) a downgraded device cannot deliver.
- Variant tests now run on runTest virtual time instead of racing the
  real 1-second history-fallback timer, and cancel it via
  handleDisconnected(); new regression tests cover the clone fallback,
  the mid-session supportFor() query, and the persisted override.

All changes stay within MiScaleHandler and its test, per the
how-to-support-a-new-scale ground rules (no shared infrastructure
changes).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HkiN1TxugYUaxWx2HbzhbD
@l9i l9i changed the title MiScaleHandler: detect v1/v2 variant from GATT table, fail fast when neither primary service exists MiScaleHandler: derive the v1/v2 variant from the GATT table instead of trusting the scan-time name heuristic Aug 26, 2026
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.

Mi Scale v2 driver hangs in "waiting for notification" on weight-only Mi Smart Scale 2 (XMTZC04HM)

2 participants