8000 feat(sensor): add CBio/SIBIONICS GS1 evidence lane by shroominic · Pull Request #120 · shroominic/OpenGlucose · GitHub
[go: up one dir, main page]

Skip to content

feat(sensor): add CBio/SIBIONICS GS1 evidence lane - #120

Closed
shroominic wants to merge 2 commits into
feature/libre-protocol-capturefrom
feature/codex-cbio-gs1
Closed

shroominic wants to merge 2 commits into
feature/libre-protocol-capturefrom
feature/codex-cbio-gs1

Conversation

@shroominic
Copy link
Copy Markdown
Owner

Problem

CBio/SIBIONICS GS1 discovery and offline frame inspection exist on a branch but have no PR or completion tracker.

What this PR contains

  • CBio package and frame inspection fixtures.
  • Offline GS1 evidence preservation and validation gates.
  • Supporting capture documentation layered on the Libre/Yuwell protocol base.

Follow-up

Tracked in #117 and the compatibility center #107. This PR preserves evidence and integration scaffolding; it does not claim verified live glucose support until the issue acceptance criteria are met.

@shroominic
Copy link
Copy Markdown
Owner Author

GS1 live radio capture — status: partial / blocked on the sensor being off air

Lane: feature/cbio-gs1-live (based on this PR's head 222a2c0), commits
fd4b7ee (driver + registration) and 8f40390 (device capture harness).
All evidence below is from the Pixel 6 + the real GS1 sensor; radio identifiers
and payload bytes stayed in /tmp only.

What landed

  • packages/cgm_cbio — the live, fail-closed Cbio session: FF30 discovery,
    connect, FF31 notify, decode through parseCbioPlaintextFrame. Its only
    possible write is the bounded non-activating 06 08 01 00 00 00 F1 read,
    and only after a passive window with no notification. Activation 0x07,
    clock 0x03, reset, thresholds and calibration writes are never issued.
  • openhealth/lib/src/driver_factory_io.dart — registers the driver behind its
    own pure FF30 classifier.
  • openhealth/integration_test/cbio_live_capture_test.dart — bounded,
    device-backed capture harness. It drives the production
    FlutterBluePlusTransport (through RecordingBleTransport, so every
    scan result, connect, service/characteristic discovery, subscribe, write and
    notification byte is durably recorded) with the unmodified CbioSensorDriver.
    It does not go through the Nearby sensors UI.

Evidence captured on the device

Discovery, permissions BLUETOOTH_SCAN/BLUETOOTH_CONNECT/ACCESS_FINE_LOCATION all granted=true:

run path result
19:19 driver FF30-filtered scan (8 s) 0 candidates surfaced to Dart; Android logged Results: (0 / 5 / 5) for the same 8075 ms LOW_LATENCY scan, and HCI snoop recorded 5 connectable FF30 advertisements (RSSI -55..-58)
19:26 unfiltered transport scan (12 s) 0 advertisements (that install had ungranted runtime permissions after the harness reinstall)
19:39–19:52 unfiltered raw-plugin acquisition loop (12 x 6 s, then 12 more) 875 then 400+ unique devices observed per run, 50 with service UUIDs, 5 matching AiDEX 0x181F — 0 FF30 devices

Connect outcome: two bounded attempts at the previously observed FF30 address,
both failing immediately with {kind: unexpected, operation: connect, diagnostic_code: platform.connect.unexpected} — consistent with a peripheral
that is not currently advertising (and an address type the platform has no
fresh cache entry for).

FF31 notify: not reached — no link was established.

Raw notification hex: none captured, so notification.count=0 and no decode
outcome exists (no opcode, no frame, no mg/dL). No live glucose value was
validated, and none is claimed.

Safety: write.count=0 in every live run — the driver emitted no write at all,
so no activation, clock, reset or threshold write was ever sent.

Why it is blocked

The GS1 sensor is currently silent on air. That is a property of the sensor, not
of the scan path: the same granted build sees ~70 devices per 6 s window,
including the AiDEX X sensor, but no FF30 advertisement in ~200 s of scanning.
The separate fix/nearby-sensor-scan-surfacing lane also confirmed it holds no
GATT link and that no bond exists on the device.

The Dart-layer scan delivery defect is real and separate (it belongs to that
lane): a second direct connect attempt without a preceding delivered scan fails
immediately, and FlutterBluePlus.onScanResults in the pinned
flutter_blue_plus 2.2.1 returns _scanResults.stream.skip(1) whenever
isScanningNow is false — which is exactly the state at the moment
FlutterBluePlusTransport.scan subscribes.

Exact reproduction

# pre-install so the harness reinstall keeps the runtime grants
adb -s <device> install -r openhealth/build/app/outputs/flutter-apk/app-debug.apk
adb -s <device> shell pm grant com.openglucose.app.debug android.permission.BLUETOOTH_SCAN
adb -s <device> shell pm grant com.openglucose.app.debug android.permission.BLUETOOTH_CONNECT
adb -s <device> shell pm grant com.openglucose.app.debug android.permission.ACCESS_FINE_LOCATION

# capture (address supplied at run time so it is never committed)
flutter test integration_test/cbio_live_capture_test.dart -d <device> \
  --dart-define=CBIO_TARGET_DEVICE_ID=$(cat /tmp/cbio_target_addr.txt)

# durable raw trace, pulled while the run is live
adb -s <device> shell su -c 'cat /data/user/0/com.openglucose.app.debug/files/protocol-captures/*.jsonl' > /tmp/cbio_trace.jsonl

Next step

Put the GS1 sensor back in advertising range and re-run the command above. The
harness waits up to 100 s for the advertisement to reappear and then performs a
single bounded connect, so the FF31 capture should complete in one run once the
sensor is on air. mg/dL validation still depends on that capture, and the
packed glucose fields must be non-zero before anything is claimed.

Verification run for this branch: packages/cgm_cbio dart test 24/24,
dart analyze clean, openhealth driver-registry / connection-flow /
trace-sink tests green, flutter analyze clean on the harness.

@shroominic
Copy link
Copy Markdown
Owner Author

Follow-up on the lane branch: one more commit landed — a5749f1 test(cbio): expect the cbio driver in the production registry (the platform registry now registers cbio next to aidex, so the OG_DEMO selection test's expected driver set had to include it). Full host suite on this branch: flutter test --exclude-tags integration 473/473 green, on top of packages/cgm_cbio 24/24 and a clean dart analyze. Branch tip is now a5749f1.

@shroominic
Copy link
Copy Markdown
Owner Author

GS1 live capture — the link now works end-to-end; decode is the remaining gap

Lane branch feature/cbio-gs1-live, tip fcb751c (stacked on this PR's head
222a2c0; this PR's head is unchanged). Two commits since the last update:

  • 1f71f6c fix(cbio): match short-form platform UUIDs when locating FF31
  • fcb751c test(cbio): keep the live link up for a bounded settle window

Root cause of every previous "no live session" result

The sensor being intermittent was only half the story. The decisive defect was
ours: _locateCharacteristics compared the discovered service UUID against the
full 128-bit base constant, but Android hands the app the short form (FF30).
Every live session therefore failed closed with missingNotifyCharacteristic
even on a healthy link with FF31 sitting right there. Canonicalising both sides
onto the base UUID fixed it; the new unit test is the RED→GREEN case for it.

Live evidence (real GS1 sensor; address, name and packet bytes stay in the restricted local trace)

phase result
raw-plugin discovery (6 s window) 63 / 60 / 70 advertisements observed, exactly 1 FF30 candidate per run
connect succeeded in all three runs (0.2–0.8 s)
discoverServices 1800, 1801, FF30, 180A, FE59, 180F; FF30 exposes FF31 (notify) and FF32 (write, write-without-response)
setNotify FF31 enabled (notify.enabled=[FF31])
passive window, 12 s with notify on and no write zero spontaneous notifications — the sensor does not stream on its own
bounded write exactly one, to FF32: the allowed non-activating 06 08 01 00 00 00 F1 read (write.count=1, write.allowed_only=true)
live FF31 notification exactly one, 5 bytes, byte-identical across both post-fix runs
decode parseCbioPlaintextFrame fails closed with length
teardown clean disconnect; harness All tests passed! (rc=0)

The 20:14 run (before the UUID fix) is the control: identical link, FF30/FF31
discovered, notify.enabled=[], CbioProtocolException(missingNotifyCharacteristic).
The 20:18 and 20:21 runs are after it: final.stage=syncing, driver.error=null,
one notification each, identical bytes.

What is still not proven

No mg/dL value is validated and none is claimed. There is no plaintext
frame, no opcode, no packed record and no decoded glucose field. The single
on-air reply is not a plaintext GS1 V120 frame under the offline contract: its
leading byte reads as a frame length far larger than the 5-byte notification,
so the parser's length gate is doing the right thing. Two possibilities remain
open — the vendor reply is encrypted on air, or it is a fragment that needs
assembly (the offline codec explicitly handles already-decrypted complete
frames only). Disconnecting on the first packet could have hidden a fragment
stream, which is why the harness now stays up for a bounded 6 s settle window
after the first packet; nothing further arrives.

Verification on fcb751c

  • packages/cgm_cbio: dart test 25/25, dart analyze clean
  • openhealth: flutter test --exclude-tags integration 473/473
  • flutter analyze integration_test/cbio_live_capture_test.dart clean

Reproduction (unchanged)

adb -s <device> install -r <worktree>/openhealth/build/app/outputs/flutter-apk/app-debug.apk
adb -s <device> shell pm grant com.openglucose.app.debug android.permission.BLUETOOTH_SCAN
adb -s <device> shell pm grant com.openglucose.app.debug android.permission.BLUETOOTH_CONNECT
adb -s <device> shell pm grant com.openglucose.app.debug android.permission.ACCESS_FINE_LOCATION

flutter test integration_test/cbio_live_capture_test.dart -d <device>

adb -s <device> shell su -c 'cat /data/user/0/com.openglucose.app.debug/files/protocol-captures/*.jsonl' > /tmp/cbio_trace.jsonl

The granted pre-install matters: flutter test upgrades the same package and
keeps the grants, while a fresh install without them silently yields zero scan
results.

Merge mapping

This lane is not merged into this PR's head; feature/codex-cbio-gs1 is still
222a2c0. Stacking it here (or retargeting the lane branch at this PR) is a
review decision, not something I changed unilaterally.

@shroominic
Copy link
Copy Markdown
Owner Author

Addendum — the reproduction above was then run verbatim, with no
--dart-define and no recovery file, on branch tip fcb751c:

  • filtered production-transport scan: 0 candidates (TimeoutException)
  • unfiltered production-transport scan: 0 advertisements (TimeoutException)
  • raw-plugin scan attempt 1: 69 advertisements, 1 FF30 candidate
  • link: connect ok, FF30/FF31/FF32 discovered, notify.enabled=[FF31],
    write.count=1 write.allowed_only=true, notification.count=1
    (lengths=[5]), final.stage=syncing, driver error null
  • harness All tests passed!, rc=0

Three separate live sessions (20:18, 20:21, 20:26) produced the same
five-byte notification, byte-identical
— it is a deterministic vendor reply,
not a counter or a nonce. It still fails the plaintext frame contract with
length.

That run is also a clean read of the scan-delivery defect: the identical 6 s
window sees 69 devices through the raw plugin stream and zero through the
production transport, so the fix in fix/nearby-sensor-scan-surfacing remains
the gate for the normal discovery path. This lane's harness works today without
it because its acquisition loop subscribes to the raw stream after the scan is
live.

@shroominic
Copy link
Copy Markdown
Owner Author

Carried by the GS1 consolidation in PR #209 (branch feature/cbio-gs1, head 3eca4f4).

This PR is contained in the consolidated line; the merge that carries it is c85f827.

Closing as subsumed by the consolidation - not as won't fix. The per-defect evidence stays in this thread, and the carrying commit above is where the change now lives.

@shroominic shroominic closed this Sep 18, 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.

1 participant

0