diff --git a/plugins/sync/src/main/kotlin/app/aaps/plugins/sync/garmin/GarminPlugin.kt b/plugins/sync/src/main/kotlin/app/aaps/plugins/sync/garmin/GarminPlugin.kt index 81b03f98c5..1a37a3ae4b 100644 --- a/plugins/sync/src/main/kotlin/app/aaps/plugins/sync/garmin/GarminPlugin.kt +++ b/plugins/sync/src/main/kotlin/app/aaps/plugins/sync/garmin/GarminPlugin.kt @@ -327,6 +327,14 @@ class GarminPlugin @Inject constructor( GlucoseUnit.MGDL -> jo.addProperty("units_hint", "mgdl") GlucoseUnit.MMOL -> jo.addProperty("units_hint", "mmol") } + jo.addProperty("iob", loopHub.insulinOnboard + loopHub.insulinBasalOnboard) + loopHub.temporaryBasal.also { + if (!it.isNaN()) { + val temporaryBasalRateInPercent = (it * 100.0).toInt() + jo.addProperty("tbr", temporaryBasalRateInPercent) + } + } + jo.addProperty("cob", loopHub.carbsOnboard) } joa.add(jo) } diff --git a/plugins/sync/src/main/kotlin/app/aaps/plugins/sync/garmin/LoopHub.kt b/plugins/sync/src/main/kotlin/app/aaps/plugins/sync/garmin/LoopHub.kt index 4b420d21f5..ebd8ac209b 100644 --- a/plugins/sync/src/main/kotlin/app/aaps/plugins/sync/garmin/LoopHub.kt +++ b/plugins/sync/src/main/kotlin/app/aaps/plugins/sync/garmin/LoopHub.kt @@ -20,6 +20,12 @@ interface LoopHub { /** Returns the remaining bolus insulin on board. */ val insulinOnboard: Double + /** Returns the basal insulin on board. */ + val insulinBasalOnboard: Double + + /** Returns the remaining carbs on board. */ + val carbsOnboard: Double? + /** Returns true if the pump is connected. */ val isConnected: Boolean @@ -48,4 +54,4 @@ interface LoopHub { avgHeartRate: Int, device: String? ) -} \ No newline at end of file +} diff --git a/plugins/sync/src/main/kotlin/app/aaps/plugins/sync/garmin/LoopHubImpl.kt b/plugins/sync/src/main/kotlin/app/aaps/plugins/sync/garmin/LoopHubImpl.kt index dfcf9dab37..1e9f19ae7f 100644 --- a/plugins/sync/src/main/kotlin/app/aaps/plugins/sync/garmin/LoopHubImpl.kt +++ b/plugins/sync/src/main/kotlin/app/aaps/plugins/sync/garmin/LoopHubImpl.kt @@ -13,6 +13,7 @@ import app.aaps.core.interfaces.profile.ProfileFunction import app.aaps.core.interfaces.pump.DetailedBolusInfo import app.aaps.core.interfaces.queue.CommandQueue import app.aaps.core.interfaces.sharedPreferences.SP +import app.aaps.core.main.graph.OverviewData import app.aaps.database.ValueWrapper import app.aaps.database.entities.EffectiveProfileSwitch import app.aaps.database.entities.GlucoseValue @@ -42,6 +43,7 @@ class LoopHubImpl @Inject constructor( private val repo: AppRepository, private val userEntryLogger: UserEntryLogger, private val sp: SP, + private val overviewData: OverviewData, ) : LoopHub { @VisibleForTesting @@ -64,6 +66,14 @@ class LoopHubImpl @Inject constructor( override val insulinOnboard: Double get() = iobCobCalculator.calculateIobFromBolus().iob + /** Returns the remaining bolus and basal insulin on board. */ + override val insulinBasalOnboard :Double + get() = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().basaliob + + /** Returns the remaining carbs on board. */ + override val carbsOnboard: Double? + get() = overviewData.cobInfo(iobCobCalculator).displayCob + /** Returns true if the pump is connected. */ override val isConnected: Boolean get() = !loop.isDisconnected @@ -142,4 +152,4 @@ class LoopHubImpl @Inject constructor( ) repo.runTransaction(InsertOrUpdateHeartRateTransaction(hr)).blockingAwait() } -} \ No newline at end of file +} diff --git a/plugins/sync/src/test/kotlin/app/aaps/plugins/sync/garmin/GarminPluginTest.kt b/plugins/sync/src/test/kotlin/app/aaps/plugins/sync/garmin/GarminPluginTest.kt index 6913ad4609..447788146e 100644 --- a/plugins/sync/src/test/kotlin/app/aaps/plugins/sync/garmin/GarminPluginTest.kt +++ b/plugins/sync/src/test/kotlin/app/aaps/plugins/sync/garmin/GarminPluginTest.kt @@ -57,6 +57,10 @@ class GarminPluginTest: TestBase() { @AfterEach fun verifyNoFurtherInteractions() { verify(loopHub, atMost(2)).currentProfileName + verify(loopHub, atMost(3)).insulinOnboard + verify(loopHub, atMost(3)).insulinBasalOnboard + verify(loopHub, atMost(3)).temporaryBasal + verify(loopHub, atMost(3)).carbsOnboard verifyNoMoreInteractions(loopHub) } @@ -221,14 +225,18 @@ class GarminPluginTest: TestBase() { } @Test - fun onSgv_NoDelta() { +fun onSgv_NoDelta() { whenever(loopHub.glucoseUnit).thenReturn(GlucoseUnit.MMOL) + whenever(loopHub.insulinOnboard).thenReturn(2.7) + whenever(loopHub.insulinBasalOnboard).thenReturn(2.5) + whenever(loopHub.temporaryBasal).thenReturn(0.8) + whenever(loopHub.carbsOnboard).thenReturn(10.7) whenever(loopHub.getGlucoseValues(any(), eq(false))).thenReturn( listOf(createGlucoseValue( clock.instant().minusSeconds(100L), 99.3))) assertEquals( - """[{"_id":"-900000","device":"RANDOM","deviceString":"1969-12-31T23:58:30Z","sysTime":"1969-12-31T23:58:30Z","unfiltered":90.0,"date":-90000,"sgv":99,"direction":"Flat","noise":4.5,"units_hint":"mmol"}]""", - gp.onSgv(mock(), createUri(mapOf()), null)) + """[{"_id":"-900000","device":"RANDOM","deviceString":"1969-12-31T23:58:30Z","sysTime":"1969-12-31T23:58:30Z","unfiltered":90.0,"date":-90000,"sgv":99,"direction":"Flat","noise":4.5,"units_hint":"mmol","iob":5.2,"tbr":80,"cob":10.7}]""", + gp.onSgv(mock(), createUri(mapOf()), null)) verify(loopHub).getGlucoseValues(clock.instant().minusSeconds(25L * 300L), false) verify(loopHub).glucoseUnit } @@ -236,26 +244,31 @@ class GarminPluginTest: TestBase() { @Test fun onSgv() { whenever(loopHub.glucoseUnit).thenReturn(GlucoseUnit.MMOL) + whenever(loopHub.insulinOnboard).thenReturn(2.7) + whenever(loopHub.insulinBasalOnboard).thenReturn(2.5) + whenever(loopHub.temporaryBasal).thenReturn(0.8) + whenever(loopHub.carbsOnboard).thenReturn(10.7) whenever(loopHub.getGlucoseValues(any(), eq(false))).thenAnswer { i -> val from = i.getArgument(0) fromClosedRange(from.toEpochMilli(), clock.instant().toEpochMilli(), 300_000L) .map(Instant::ofEpochMilli) .mapIndexed { idx, ts -> createGlucoseValue(ts, 100.0+(10 * idx)) }.reversed()} assertEquals( - """[{"_id":"100000","device":"RANDOM","deviceString":"1970-01-01T00:00:10Z","sysTime":"1970-01-01T00:00:10Z","unfiltered":90.0,"date":10000,"sgv":120,"delta":10,"direction":"Flat","noise":4.5,"units_hint":"mmol"}]""", + """[{"_id":"100000","device":"RANDOM","deviceString":"1970-01-01T00:00:10Z","sysTime":"1970-01-01T00:00:10Z","unfiltered":90.0,"date":10000,"sgv":120,"delta":10,"direction":"Flat","noise":4.5,"units_hint":"mmol","iob":5.2,"tbr":80,"cob":10.7}]""", gp.onSgv(mock(), createUri(mapOf("count" to "1")), null)) verify(loopHub).getGlucoseValues( clock.instant().minusSeconds(600L), false) + assertEquals( - """[{"_id":"100000","device":"RANDOM","deviceString":"1970-01-01T00:00:10Z","sysTime":"1970-01-01T00:00:10Z","unfiltered":90.0,"date":10000,"sgv":130,"delta":10,"direction":"Flat","noise":4.5,"units_hint":"mmol"},""" + - """{"_id":"-2900000","device":"RANDOM","deviceString":"1969-12-31T23:55:10Z","sysTime":"1969-12-31T23:55:10Z","unfiltered":90.0,"date":-290000,"sgv":120,"delta":10,"direction":"Flat","noise":4.5}]""", + """[{"_id":"100000","device":"RANDOM","deviceString":"1970-01-01T00:00:10Z","sysTime":"1970-01-01T00:00:10Z","unfiltered":90.0,"date":10000,"sgv":130,"delta":10,"direction":"Flat","noise":4.5,"units_hint":"mmol","iob":5.2,"tbr":80,"cob":10.7},""" + + """{"_id":"-2900000","device":"RANDOM","deviceString":"1969-12-31T23:55:10Z","sysTime":"1969-12-31T23:55:10Z","unfiltered":90.0,"date":-290000,"sgv":120,"delta":10,"direction":"Flat","noise":4.5}]""", gp.onSgv(mock(), createUri(mapOf("count" to "2")), null)) verify(loopHub).getGlucoseValues( clock.instant().minusSeconds(900L), false) assertEquals( - """[{"date":10000,"sgv":130,"delta":10,"direction":"Flat","noise":4.5,"units_hint":"mmol"},""" + + """[{"date":10000,"sgv":130,"delta":10,"direction":"Flat","noise":4.5,"units_hint":"mmol","iob":5.2,"tbr":80,"cob":10.7},""" + """{"date":-290000,"sgv":120,"delta":10,"direction":"Flat","noise":4.5}]""", gp.onSgv(mock(), createUri(mapOf("count" to "2", "brief_mode" to "true")), null)) verify(loopHub, times(2)).getGlucoseValues( diff --git a/plugins/sync/src/test/kotlin/app/aaps/plugins/sync/garmin/LoopHubTest.kt b/plugins/sync/src/test/kotlin/app/aaps/plugins/sync/garmin/LoopHubTest.kt index a88facfa05..c96bcaee4b 100644 --- a/plugins/sync/src/test/kotlin/app/aaps/plugins/sync/garmin/LoopHubTest.kt +++ b/plugins/sync/src/test/kotlin/app/aaps/plugins/sync/garmin/LoopHubTest.kt @@ -5,6 +5,7 @@ import app.aaps.core.interfaces.aps.Loop import app.aaps.core.interfaces.constraints.Constraint import app.aaps.core.interfaces.constraints.ConstraintsChecker import app.aaps.core.interfaces.db.GlucoseUnit +import app.aaps.core.interfaces.iob.CobInfo import app.aaps.core.interfaces.iob.IobCobCalculator import app.aaps.core.interfaces.iob.IobTotal import app.aaps.core.interfaces.logging.UserEntryLogger @@ -13,6 +14,7 @@ import app.aaps.core.interfaces.profile.ProfileFunction import app.aaps.core.interfaces.pump.DetailedBolusInfo import app.aaps.core.interfaces.queue.CommandQueue import app.aaps.core.interfaces.sharedPreferences.SP +import app.aaps.core.main.graph.OverviewData import app.aaps.database.ValueWrapper import app.aaps.database.entities.EffectiveProfileSwitch import app.aaps.database.entities.GlucoseValue @@ -54,6 +56,7 @@ class LoopHubTest: TestBase() { @Mock lateinit var repo: AppRepository @Mock lateinit var userEntryLogger: UserEntryLogger @Mock lateinit var sp: SP + @Mock lateinit var overviewData: OverviewData private lateinit var loopHub: LoopHubImpl private val clock = Clock.fixed(Instant.ofEpochMilli(10_000), ZoneId.of("UTC")) @@ -62,7 +65,7 @@ class LoopHubTest: TestBase() { fun setup() { loopHub = LoopHubImpl( aapsLogger, commandQueue, constraints, iobCobCalculator, loop, - profileFunction, repo, userEntryLogger, sp + profileFunction, repo, userEntryLogger, sp, overviewData ) loopHub.clock = clock } @@ -76,9 +79,10 @@ class LoopHubTest: TestBase() { verifyNoMoreInteractions(profileFunction) verifyNoMoreInteractions(repo) verifyNoMoreInteractions(userEntryLogger) + verifyNoMoreInteractions(overviewData) } - @Test +@Test fun testCurrentProfile() { val profile = mock(Profile::class.java) `when`(profileFunction.getProfile()).thenReturn(profile) @@ -109,6 +113,22 @@ class LoopHubTest: TestBase() { verify(iobCobCalculator, times(1)).calculateIobFromBolus() } + @Test + fun testBasalOnBoard() { + val iobBasal = IobTotal(time = 0).apply { basaliob = 23.9 } + `when`(iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended()).thenReturn(iobBasal) + assertEquals(23.9, loopHub.insulinBasalOnboard, 1e-10) + verify(iobCobCalculator, times(1)).calculateIobFromTempBasalsIncludingConvertedExtended() + } + + @Test + fun testCarbsOnBoard() { + val cobInfo = CobInfo(0, 12.0, 0.0) + `when`(overviewData.cobInfo(iobCobCalculator)).thenReturn(cobInfo) + assertEquals(12.0, loopHub.carbsOnboard) + verify(overviewData, times(1)).cobInfo(iobCobCalculator) + } + @Test fun testIsConnected() { `when`(loop.isDisconnected).thenReturn(false) @@ -247,4 +267,4 @@ class LoopHubTest: TestBase() { samplingStart, samplingEnd, 101, "Test Device") verify(repo).runTransaction(InsertOrUpdateHeartRateTransaction(hr)) } -} \ No newline at end of file +}