feat: Eversense E3/365 CGM integration with AndroidAPS#4869
Conversation
| <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> | ||
| <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> | ||
|
|
||
| <application> |
|
There are many issues with this PR. Let me pick some of them:
|
…ompat - Clean up Handler callbacks in disconnect/cleanUp/onStop to prevent stacked reconnects - Dispatch triggerFullSync, readSignalStrength, setDiagnosticMode, writeSettings to bleExecutor - Fix deprecated writeCharacteristic API for Android 13+ (API 33) - Add onCharacteristicWrite callback for write failure detection - Cancel ioScope in onStop to prevent leaked coroutines - Make watchers thread-safe with CopyOnWriteArrayList - Shutdown and recreate networkExecutor in cleanUp - Add @singleton annotation to EversensePlugin - Fix wrong TAG in Eversense365Communicator - Remove dead code (if false return)
…ingleton - Convert EversenseCGMPlugin from manual singleton to constructor-injected class - Remove setContext() — Context, BluetoothManager, SharedPreferences now non-nullable vals - Provide EversenseCGMPlugin via @provides @singleton in SourceModule - Inject into EversensePlugin, CalibrationActivity, StatusActivity, PlacementActivity - Add @androidentrypoint to PlacementActivity - Eliminate ~40 unnecessary null checks (fields no longer nullable)
9cc25bc to
2a91517
Compare
|
Hi @MilosKozak, thank you for the review. You're right on all three points — I've pushed a reworked version that addresses each one. Code quality — memory leaks, threading: I audited the full codebase and found the issues you were pointing at. Handler callbacks in On the threading side, All fixed — plus added v4 design:
Attribution: This was a fair point. The core BLE driver and protocol implementation were built by @n0rb33r7 and @bastiaanv in PR #4474. I squashed the branch into a single commit with proper I know there's still work to do — the Appreciate the thorough review. |
|
if @bastiaanv clearly confirm here, it's ok for him, I can accept it |
cbeb845 to
f631aeb
Compare
|
@CAPTCG Once you have shared your working branch with a PR, you should stop to "force push" your updates within your one single commit, otherwise all the reviewers and all the other developers will have to restart from scratch on each update, and nobody can work in parallel to help you... Just do simple commits (one additional new commit for each topic), and push them (the others can easily see what has been updated and check the detailed improvements on each new commit) |
|
@Philoul I misunderstood the last comment from @MilosKozak . I will correct the single commit and proceed as you have described. Thank you for the guidance. |
|
Milos reviewed 7h ago all the content of your PR, using the first commit 9cc25bc. When you replaced your first commit by the commit 2a91517, he will have to review everything again and again on each new "forced push", because it's impossible to see what has been updated between your first, your second and all the other versions. That's why it's important to never "force push" updates after a PR, but only push your step by step improvements with dedicated additional commits. |
f631aeb to
9cc25bc
Compare
- Remove unused variables and methods - Add no-op comments to empty interface defaults - Make EversenseScanCallback a functional interface - Remove useless null checks on non-nullable writePacket results - Extract Content-Type and application/json string constants - Fix useless null-safe access on non-nullable state
- Replace hardcoded notification strings with rh.gs(R.string.xxx)
- Replace hardcoded status labels with getString(R.string.xxx)
- Add 21 new string resources for Crowdin i18n support
- Replace Thread { }.start() with CoroutineScope in activities
- Cancel coroutine scopes in onDestroy()
|
I think we have an overlap between Calibration plugin included within AAPS and the internal Eversense calibration included within this PR.
|
|
@Philoul The Eversense calibrate code is an individualized working method to E3 or E365. The Eversense calibration code differs between the E3 180-day transmitter and the E365-day transmitter vastly. The Eversense Plugin identifies the transmitter when the transmitter is pared/connected as E3 or E365. After that connection to the transmitter is made, the code takes a different path for calibrating the E3 vs the E365. The existing calibration code in AASP v4 for other CGM's is not compatible for calibrating the Eversence transmitters. |
|
Sorry if I misunderstood how it works...
|
|
I can explain the main differences. The E3 requires a calibration each day after initialization. The E365 requires a calibration each week after initialization. If a calibration is not received by the transmitter in each case at the required time the readings will stop. The other CGM's let you calibrate if you need to. The readings with the other CGM's continue even if you never calibrate. This is the main difference. The Eversense transmitter communicates calibration data over a proprietary BLE protocol that differs between E3 and E365 hardware. The plugin identifies which transmitter model is paired during the initial connection, then routes calibration commands through the correct protocol path. The calibration values aren't just stored locally — they must be sent to the transmitter in a format it accepts, or it rejects them. The existing AAPS calibration plugin writes calibration entries to the database but doesn't handle the Eversense-specific BLE handshake required to deliver them to the transmitter. |
….eversense - Move all source files to app.aaps.plugins.eversense package - Update all package declarations and import statements - Update namespace in build.gradle.kts - Remove UTF-8 BOM characters from source files - Align with AAPS convention: all plugins use app.aaps.plugins.* namespace
- Rewrite EversenseLogger as a thin bridge that delegates to AAPSLogger with LTag.BGSOURCE, matching AAPS logging conventions - Remove custom Logback configuration and direct logback dependency - Initialize bridge via EversenseLogger.init(aapsLogger) in EversensePlugin.onStart() - Remove unused loggingEnabled constructor parameter - All 215 existing log call sites work unchanged through the bridge - Logging now visible in AAPS log filtering with LTag.BGSOURCE
- Replace // NOSONAR comments with @Suppress("kotlin:S6418") annotations that SonarCloud's Kotlin analyzer actually recognizes - CLIENT_ID and CLIENT_SECRET are public values extracted from the official Eversense Android APK — identical for all users, not personal secrets - Fixes Quality Gate failure: Security Rating C -> A
|
Three commits pushed addressing pattern alignment:
|
- Add android:usesCleartextTraffic=false to application tag - Resolves SonarCloud Security Hotspot (xml:S5332)
…t on sensor - Add 5-reading smoothing buffer with median filter to PlacementActivity - Require at least 3 consistent readings before displaying signal bars - Detect noise (signal range > 40%) and show 'Searching for sensor...' instead of fluctuating random bars - When transmitter is not positioned over the sensor, signal readings are BLE RSSI noise, not actual sensor coupling strength - Add string resources for the not-on-sensor state
…redDevice on disconnect
…entries in new format)
|
Update: Three fixes pushed + upstream merge
Replaced raw setDiagnosticMode(true/false) calls with enterPositioningMode()/exitPositioningMode() and moved from onCreate/onDestroy to onResume/onPause. This sets the isPositioningMode flag so diagnostic mode is automatically re-enabled after BLE reconnects during placement. Without this, the E3 transmitter would lose diagnostic mode on each reconnect cycle and the signal strength display would stop updating. Tested on both E3 and E365. Previously, tapping Disconnect in the status screen cleared the stored device address, forcing a 10-second BLE scan on the next Connect. Now it just disconnects — reconnect uses the stored address and connects immediately. |
…recated SAGE keys
|




This PR adds full Eversense E3 (180-day) and Eversense 365 (1-year) CGM support to AndroidAPS, building on the foundation started in #4474 by @n0rb33r7 and @bastiaanv.
@MilosKozak @bastiaanv @n0rb33r7 — would appreciate your review.
Why This Matters
AndroidAPS users deserve choices. The Eversense CGM is the only long-term implantable continuous glucose monitor available — the E3 lasts 180 days and the 365 lasts a full year without replacement. For people who have chosen Eversense for its longevity, accuracy, and convenience, the absence of AndroidAPS support means they are locked out of closed-loop insulin delivery entirely. This is not a niche device — Eversense is FDA-approved, clinically validated, and used by thousands of people with Type 1 diabetes worldwide who deserve the same access to automated insulin delivery that Dexcom and Libre users enjoy today.
Merging this PR closes that gap. It brings Eversense users into the AndroidAPS ecosystem with a fully working, production-tested driver for both the E3 and 365 transmitters. Once the initial sensor initialization is completed using the official Eversense app, AndroidAPS takes over completely — no further dependency on the official app for either device.
This is not a work-in-progress. It has been tested in real-world use on multiple devices and firmware versions, all unit tests pass, and the build is clean.
What's Included
Eversense E3
Eversense 365
Core Integration
Test Results
All unit tests pass against the latest upstream dev branch (f7bcb0e):
Real-World Testing
Code Quality
Special Thanks
A heartfelt thank you to Paolo (Italy) for his extraordinary dedication as our Eversense E3 tester. Paolo invested countless hours running test builds, reporting results, calibrating under every condition, and providing the kind of precise real-world feedback that made this driver possible. His patience and commitment throughout the development process were invaluable — this would not have been the same without him.
Related