Merge pull request #3082 from swissalpine/garmin-sgv-json

Add iob, cob, tbr to GARMIN sgv.json endpoint
This commit is contained in:
Milos Kozak 2023-12-01 16:47:02 +01:00 committed by GitHub
commit 3cdf73ce46
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 69 additions and 12 deletions

View file

@ -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)
}

View file

@ -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

View file

@ -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

View file

@ -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)
}
@ -223,11 +227,15 @@ class GarminPluginTest: TestBase() {
@Test
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"}]""",
"""[{"_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<Instant>(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":"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(

View file

@ -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,6 +79,7 @@ class LoopHubTest: TestBase() {
verifyNoMoreInteractions(profileFunction)
verifyNoMoreInteractions(repo)
verifyNoMoreInteractions(userEntryLogger)
verifyNoMoreInteractions(overviewData)
}
@Test
@ -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)