Merge remote-tracking branch 'Nightscout/dev' into wear/new_custom_watchface

This commit is contained in:
Philoul 2023-08-13 22:56:04 +02:00
commit 9cd166f802
201 changed files with 12112 additions and 1005 deletions

View file

@ -2,6 +2,7 @@
<dictionary name="project-dictionary"> <dictionary name="project-dictionary">
<words> <words>
<w>aaps</w> <w>aaps</w>
<w>aapsclient</w>
<w>abcdef</w> <w>abcdef</w>
<w>accu</w> <w>accu</w>
<w>acked</w> <w>acked</w>
@ -18,11 +19,13 @@
<w>bage</w> <w>bage</w>
<w>basaliob</w> <w>basaliob</w>
<w>basals</w> <w>basals</w>
<w>batt</w>
<w>bgcheck</w> <w>bgcheck</w>
<w>bgsource</w> <w>bgsource</w>
<w>boluscalc</w> <w>boluscalc</w>
<w>bolusing</w> <w>bolusing</w>
<w>boluswizard</w> <w>boluswizard</w>
<w>boyda</w>
<w>carb</w> <w>carb</w>
<w>carbratio</w> <w>carbratio</w>
<w>carbs</w> <w>carbs</w>
@ -67,6 +70,7 @@
<w>humalog</w> <w>humalog</w>
<w>iage</w> <w>iage</w>
<w>infinivocgm</w> <w>infinivocgm</w>
<w>instantiator</w>
<w>insulet</w> <w>insulet</w>
<w>intelligo</w> <w>intelligo</w>
<w>iobtotal</w> <w>iobtotal</w>
@ -78,6 +82,7 @@
<w>lyumjev</w> <w>lyumjev</w>
<w>mdtp</w> <w>mdtp</w>
<w>medtronic</w> <w>medtronic</w>
<w>medtrum</w>
<w>mgdl</w> <w>mgdl</w>
<w>miao</w> <w>miao</w>
<w>misformatted</w> <w>misformatted</w>
@ -99,6 +104,7 @@
<w>oref</w> <w>oref</w>
<w>otpauth</w> <w>otpauth</w>
<w>passcode</w> <w>passcode</w>
<w>pbage</w>
<w>pdus</w> <w>pdus</w>
<w>philoul</w> <w>philoul</w>
<w>poctech</w> <w>poctech</w>
@ -108,6 +114,7 @@
<w>pumpcontrol</w> <w>pumpcontrol</w>
<w>pumpdrivers</w> <w>pumpdrivers</w>
<w>quickwizard</w> <w>quickwizard</w>
<w>rawbg</w>
<w>readstatus</w> <w>readstatus</w>
<w>realduration</w> <w>realduration</w>
<w>refresheventsfromnightscout</w> <w>refresheventsfromnightscout</w>

View file

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
<g id="Layer_7">
<g id="Medtrum_2">
<path fill="#FFFFFF" d="M94.383,15.26H34.367c-16.327,0-29.563,13.3-29.563,29.706v38.687c0,16.406,13.236,29.706,29.563,29.706
h60.017c16.327,0,29.563-13.3,29.563-29.706V44.965C123.946,28.559,110.71,15.26,94.383,15.26z M56.935,106.393
c-1.272,0-2.303-1.031-2.303-2.303c0-1.272,1.031-2.303,2.303-2.303c1.272,0,2.303,1.031,2.303,2.303
C59.238,105.362,58.207,106.393,56.935,106.393z M56.935,26.83c-1.272,0-2.303-1.031-2.303-2.303c0-1.272,1.031-2.303,2.303-2.303
c1.272,0,2.303,1.031,2.303,2.303C59.238,25.801,58.207,26.83,56.935,26.83z M112.167,73.321V55.296
c2.428,2.241,3.95,5.448,3.95,9.013S114.595,71.081,112.167,73.321z"/>
<g>
<path fill="#E0E0E0" d="M88.268,99.547c5.196-0.229,9.454-0.602,11.926-1.147c8.841-1.951,12.243-6.224,12.243-13.385v-1.263
V44.865v-1.263c0-7.162-3.402-11.436-12.243-13.385c-2.472-0.546-6.73-0.918-11.926-1.147"/>
</g>
<g>
<path fill="#AAAAAA" d="M88.531,29.079c-19.865-0.79-52.154,0.131-58.579,1.565c-8.285,1.848-12.095,5.194-13.418,12.916
c-0.606,3.544-0.627,12.306-0.473,20.748c-0.154,8.442-0.134,17.204,0.473,20.75c1.323,7.721,5.133,11.066,13.418,12.916
c6.425,1.434,38.715,2.355,58.579,1.565"/>
</g>
<g>
<path fill="#EAEAEA" d="M88.268,98.819c5.011-0.224,9.117-0.59,11.502-1.123c8.527-1.911,11.808-6.095,11.808-13.109v-1.237
V45.267V44.03c0-7.014-3.281-11.199-11.808-13.109c-2.384-0.535-6.491-0.899-11.502-1.123"/>
</g>
<path fill="#B2B2B2" d="M88.531,29.807c-19.567-0.774-51.374,0.128-57.702,1.533c-8.161,1.81-11.914,5.087-13.217,12.649
c-0.597,3.471-0.618,12.052-0.466,20.32c-0.152,8.268-0.132,16.849,0.466,20.321c1.303,7.562,5.056,10.837,13.217,12.649
c6.329,1.405,38.135,2.306,57.702,1.533"/>
<g>
<path fill="#F0F0F0" d="M88.268,97.88c4.751-0.218,8.643-0.573,10.904-1.092c8.084-1.859,11.194-5.929,11.194-12.752v-1.203
V45.785v-1.203c0-6.823-3.11-10.895-11.194-12.752c-2.261-0.52-6.153-0.874-10.904-1.092"/>
</g>
<path fill="#B9B9B9" d="M88.531,30.745c-19.148-0.753-50.274,0.124-56.467,1.491c-7.986,1.761-11.659,4.949-12.934,12.305
c-0.585,3.377-0.604,11.724-0.456,19.767c-0.149,8.043-0.129,16.39,0.456,19.768c1.275,7.356,4.948,10.542,12.934,12.305
c6.193,1.367,37.319,2.244,56.467,1.491"/>
<g>
<path fill="#F4F4F4" d="M88.268,96.224c4.241-0.207,7.717-0.545,9.735-1.039c7.217-1.767,9.994-5.637,9.994-12.123v-1.144V46.698
v-1.144c0-6.486-2.777-10.357-9.994-12.123c-2.018-0.495-5.494-0.831-9.735-1.039"/>
</g>
<path fill="#BEBEBE" d="M88.531,32.401c-18.728-0.715-49.17,0.118-55.227,1.417c-7.811,1.674-11.403,4.705-12.65,11.698
c-0.572,3.21-0.591,11.146-0.446,18.792c-0.145,7.646-0.126,15.582,0.446,18.793c1.247,6.993,4.839,10.022,12.65,11.698
c6.057,1.299,36.499,2.133,55.227,1.417"/>
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="157.3581" y1="-156.2687" x2="184.8192" y2="-156.2687" gradientTransform="matrix(1 0 0 -1 -97.66 -91.96)">
<stop offset="0" style="stop-color:#BBBDBF"/>
<stop offset="0.1232" style="stop-color:#B5B7B9"/>
<stop offset="0.5123" style="stop-color:#B5B7B9"/>
<stop offset="0.5764" style="stop-color:#B5B7B9"/>
<stop offset="1" style="stop-color:#929497"/>
</linearGradient>
<polyline fill="url(#SVGID_1_)" stroke="#9D9DA0" stroke-width="0.4252" stroke-miterlimit="10" points="59.698,75.534
76.854,75.534 76.854,74.24 87.159,74.24 87.159,54.379 76.854,54.379 76.854,53.083 59.698,53.083 "/>
</g>
<g>
<path fill="#D0D2D3" stroke="#A3A3A3" stroke-width="0.4252" stroke-miterlimit="10" d="M90.39,68.37
c0,1.835-1.432,3.322-3.198,3.322H72.747c-1.766,0-3.198-1.487-3.198-3.322v-8.12c0-1.835,1.432-3.322,3.198-3.322h14.446
c1.766,0,3.198,1.487,3.198,3.322V68.37z"/>
</g>
<g>
<path fill="#F4F4F4" stroke="#C1C1C1" stroke-width="0.2835" stroke-miterlimit="10" d="M84.165,74.54
c0,4.387-0.05,4.448,3.627,4.448l0,0c4.387,0,7.945-3.557,7.945-7.945v-13.47c0-4.387-3.557-7.945-7.945-7.945l0,0
c-3.678,0-3.627,0.102-3.627,4.489V74.54z"/>
</g>
<g>
<g>
<path fill="#F1F1F2" stroke="#B2B2B2" stroke-width="0.2835" stroke-miterlimit="10" d="M94.571,67.677
c0,0.834-0.677,1.511-1.511,1.511l0,0c-0.834,0-1.511-0.677-1.511-1.511v-6.735c0-0.834,0.677-1.511,1.511-1.511l0,0
c0.834,0,1.511,0.677,1.511,1.511V67.677z"/>
</g>
<path fill="#231F20" d="M94.291,61.74c0,0.679-0.551,1.231-1.231,1.231l0,0c-0.679,0-1.231-0.551-1.231-1.231l0,0
c0-0.679,0.551-1.231,1.231-1.231l0,0C93.74,60.51,94.291,61.06,94.291,61.74L94.291,61.74z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

View file

@ -13,7 +13,7 @@ android {
namespace 'info.nightscout.shared.impl' namespace 'info.nightscout.shared.impl'
defaultConfig { defaultConfig {
minSdkVersion 23 // for wear minSdkVersion 25 // for wear
} }
} }

View file

@ -16,7 +16,7 @@ android {
namespace 'info.nightscout.shared' namespace 'info.nightscout.shared'
defaultConfig { defaultConfig {
minSdkVersion 23 // for wear minSdkVersion 25 // for wear
} }
} }

View file

@ -0,0 +1,3 @@
package info.nightscout.rx.events
class EventDeviceStatusChange : Event()

View file

@ -1,5 +1,3 @@
package info.nightscout.rx.events package info.nightscout.rx.events
import info.nightscout.rx.events.Event
class EventRefreshButtonState (val newState : Boolean): Event() class EventRefreshButtonState (val newState : Boolean): Event()

View file

@ -96,9 +96,9 @@ class DateUtil @Inject constructor(private val context: Context) {
return calendar.timeInMillis return calendar.timeInMillis
} }
fun toSeconds(hh_colon_mm: String): Int { fun toSeconds(hhColonMm: String): Int {
val p = Pattern.compile("(\\d+):(\\d+)( a.m.| p.m.| AM| PM|AM|PM|)") val p = Pattern.compile("(\\d+):(\\d+)( a.m.| p.m.| AM| PM|AM|PM|)")
val m = p.matcher(hh_colon_mm) val m = p.matcher(hhColonMm)
var retVal = 0 var retVal = 0
if (m.find()) { if (m.find()) {
retVal = SafeParse.stringToInt(m.group(1)) * 60 * 60 + SafeParse.stringToInt(m.group(2)) * 60 retVal = SafeParse.stringToInt(m.group(1)) * 60 * 60 + SafeParse.stringToInt(m.group(2)) * 60
@ -342,8 +342,8 @@ class DateUtil @Inject constructor(private val context: Context) {
minutes = rh.gs(R.string.shortminute) minutes = rh.gs(R.string.shortminute)
} }
var result = "" var result = ""
if (diff[TimeUnit.DAYS]!! > 0) result += diff[TimeUnit.DAYS].toString() + days if (diff.getOrDefault(TimeUnit.DAYS, -1) > 0) result += diff[TimeUnit.DAYS].toString() + days
if (diff[TimeUnit.HOURS]!! > 0) result += diff[TimeUnit.HOURS].toString() + hours if (diff.getOrDefault(TimeUnit.HOURS, -1) > 0) result += diff[TimeUnit.HOURS].toString() + hours
if (diff[TimeUnit.DAYS] == 0L) result += diff[TimeUnit.MINUTES].toString() + minutes if (diff[TimeUnit.DAYS] == 0L) result += diff[TimeUnit.MINUTES].toString() + minutes
return result return result
} }
@ -368,6 +368,7 @@ class DateUtil @Inject constructor(private val context: Context) {
if (t > 28) { if (t > 28) {
unit = rh.gs(R.string.unit_week) unit = rh.gs(R.string.unit_week)
t /= 7 t /= 7
@Suppress("KotlinConstantConditions")
if (t != 1L) unit = rh.gs(R.string.unit_weeks) if (t != 1L) unit = rh.gs(R.string.unit_weeks)
} }
} }
@ -406,8 +407,8 @@ class DateUtil @Inject constructor(private val context: Context) {
} else { } else {
thisDf = DecimalFormat("#", dfs) thisDf = DecimalFormat("#", dfs)
} }
thisDf!!.maximumFractionDigits = digits thisDf?.maximumFractionDigits = digits
return thisDf.format(x) return thisDf?.format(x) ?: ""
} }
fun formatHHMM(timeAsSeconds: Int): String { fun formatHHMM(timeAsSeconds: Int): String {
@ -432,9 +433,9 @@ class DateUtil @Inject constructor(private val context: Context) {
fun timeStampToUtcDateMillis(timestamp: Long): Long { fun timeStampToUtcDateMillis(timestamp: Long): Long {
val current = Calendar.getInstance().apply { timeInMillis = timestamp } val current = Calendar.getInstance().apply { timeInMillis = timestamp }
return Calendar.getInstance().apply { return Calendar.getInstance().apply {
set(Calendar.YEAR, current.get(Calendar.YEAR)) set(Calendar.YEAR, current[Calendar.YEAR])
set(Calendar.MONTH, current.get(Calendar.MONTH)) set(Calendar.MONTH, current[Calendar.MONTH])
set(Calendar.DAY_OF_MONTH, current.get(Calendar.DAY_OF_MONTH)) set(Calendar.DAY_OF_MONTH, current[Calendar.DAY_OF_MONTH])
}.timeInMillis }.timeInMillis
} }
@ -442,9 +443,9 @@ class DateUtil @Inject constructor(private val context: Context) {
val selected = Calendar.getInstance().apply { timeInMillis = dateUtcMillis } val selected = Calendar.getInstance().apply { timeInMillis = dateUtcMillis }
return Calendar.getInstance().apply { return Calendar.getInstance().apply {
timeInMillis = timestamp timeInMillis = timestamp
set(Calendar.YEAR, selected.get(Calendar.YEAR)) set(Calendar.YEAR, selected[Calendar.YEAR])
set(Calendar.MONTH, selected.get(Calendar.MONTH)) set(Calendar.MONTH, selected[Calendar.MONTH])
set(Calendar.DAY_OF_MONTH, selected.get(Calendar.DAY_OF_MONTH)) set(Calendar.DAY_OF_MONTH, selected[Calendar.DAY_OF_MONTH])
}.timeInMillis }.timeInMillis
} }

View file

@ -110,7 +110,7 @@ android {
defaultConfig { defaultConfig {
multiDexEnabled true multiDexEnabled true
versionCode 1500 versionCode 1500
version "3.2.0-dev-j" version "3.2.0-dev-k"
buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'
@ -213,6 +213,7 @@ dependencies {
implementation project(':pump:danar') implementation project(':pump:danar')
implementation project(':pump:diaconn') implementation project(':pump:diaconn')
implementation project(':pump:eopatch') implementation project(':pump:eopatch')
implementation project(':pump:medtrum')
implementation project(':insight') implementation project(':insight')
implementation project(':pump:medtronic') implementation project(':pump:medtronic')
implementation project(':pump:pump-common') implementation project(':pump:pump-common')

View file

@ -469,7 +469,7 @@ class MainActivity : DaggerAppCompatActivityWithResult() {
* Check for existing PasswordReset file and * Check for existing PasswordReset file and
* reset password to SN of active pump if file exists * reset password to SN of active pump if file exists
*/ */
fun passwordResetCheck(context: Context) { private fun passwordResetCheck(context: Context) {
val passwordReset = File(fileListProvider.ensureExtraDirExists(), "PasswordReset") val passwordReset = File(fileListProvider.ensureExtraDirExists(), "PasswordReset")
if (passwordReset.exists()) { if (passwordReset.exists()) {
val sn = activePlugin.activePump.serialNumber() val sn = activePlugin.activePump.serialNumber()

View file

@ -54,6 +54,7 @@ import info.nightscout.plugins.sync.xdrip.XdripPlugin
import info.nightscout.pump.combo.ComboPlugin import info.nightscout.pump.combo.ComboPlugin
import info.nightscout.pump.combov2.ComboV2Plugin import info.nightscout.pump.combov2.ComboV2Plugin
import info.nightscout.pump.diaconn.DiaconnG8Plugin import info.nightscout.pump.diaconn.DiaconnG8Plugin
import info.nightscout.pump.medtrum.MedtrumPlugin
import info.nightscout.pump.virtual.VirtualPumpPlugin import info.nightscout.pump.virtual.VirtualPumpPlugin
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventPreferenceChange import info.nightscout.rx.events.EventPreferenceChange
@ -122,6 +123,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
@Inject lateinit var wearPlugin: WearPlugin @Inject lateinit var wearPlugin: WearPlugin
@Inject lateinit var maintenancePlugin: MaintenancePlugin @Inject lateinit var maintenancePlugin: MaintenancePlugin
@Inject lateinit var eopatchPumpPlugin: EopatchPumpPlugin @Inject lateinit var eopatchPumpPlugin: EopatchPumpPlugin
@Inject lateinit var medtrumPlugin: MedtrumPlugin
@Inject lateinit var passwordCheck: PasswordCheck @Inject lateinit var passwordCheck: PasswordCheck
@Inject lateinit var nsSettingStatus: NSSettingsStatus @Inject lateinit var nsSettingStatus: NSSettingsStatus
@ -212,6 +214,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
addPreferencesFromResourceIfEnabled(medtronicPumpPlugin, rootKey, config.PUMPDRIVERS) addPreferencesFromResourceIfEnabled(medtronicPumpPlugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(diaconnG8Plugin, rootKey, config.PUMPDRIVERS) addPreferencesFromResourceIfEnabled(diaconnG8Plugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(eopatchPumpPlugin, rootKey, config.PUMPDRIVERS) addPreferencesFromResourceIfEnabled(eopatchPumpPlugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(medtrumPlugin, rootKey, config.PUMPDRIVERS)
addPreferencesFromResource(R.xml.pref_pump, rootKey, config.PUMPDRIVERS) addPreferencesFromResource(R.xml.pref_pump, rootKey, config.PUMPDRIVERS)
addPreferencesFromResourceIfEnabled(virtualPumpPlugin, rootKey) addPreferencesFromResourceIfEnabled(virtualPumpPlugin, rootKey)
addPreferencesFromResourceIfEnabled(insulinOrefFreePeakPlugin, rootKey) addPreferencesFromResourceIfEnabled(insulinOrefFreePeakPlugin, rootKey)

View file

@ -31,6 +31,7 @@ import info.nightscout.pump.common.di.PumpCommonModule
import info.nightscout.pump.dana.di.DanaHistoryModule import info.nightscout.pump.dana.di.DanaHistoryModule
import info.nightscout.pump.dana.di.DanaModule import info.nightscout.pump.dana.di.DanaModule
import info.nightscout.pump.danars.di.DanaRSModule import info.nightscout.pump.danars.di.DanaRSModule
import info.nightscout.pump.medtrum.di.MedtrumModule
import info.nightscout.pump.diaconn.di.DiaconnG8Module import info.nightscout.pump.diaconn.di.DiaconnG8Module
import info.nightscout.pump.virtual.di.VirtualPumpModule import info.nightscout.pump.virtual.di.VirtualPumpModule
import info.nightscout.rx.di.RxModule import info.nightscout.rx.di.RxModule
@ -87,6 +88,7 @@ import javax.inject.Singleton
OmnipodErosModule::class, OmnipodErosModule::class,
PumpCommonModule::class, PumpCommonModule::class,
RileyLinkModule::class, RileyLinkModule::class,
MedtrumModule::class,
VirtualPumpModule::class VirtualPumpModule::class
] ]
) )

View file

@ -46,6 +46,7 @@ import info.nightscout.plugins.sync.tidepool.TidepoolPlugin
import info.nightscout.plugins.sync.xdrip.XdripPlugin import info.nightscout.plugins.sync.xdrip.XdripPlugin
import info.nightscout.pump.combo.ComboPlugin import info.nightscout.pump.combo.ComboPlugin
import info.nightscout.pump.combov2.ComboV2Plugin import info.nightscout.pump.combov2.ComboV2Plugin
import info.nightscout.pump.medtrum.MedtrumPlugin
import info.nightscout.pump.diaconn.DiaconnG8Plugin import info.nightscout.pump.diaconn.DiaconnG8Plugin
import info.nightscout.pump.virtual.VirtualPumpPlugin import info.nightscout.pump.virtual.VirtualPumpPlugin
import info.nightscout.sensitivity.SensitivityAAPSPlugin import info.nightscout.sensitivity.SensitivityAAPSPlugin
@ -209,6 +210,12 @@ abstract class PluginsListModule {
@IntKey(156) @IntKey(156)
abstract fun bindEopatchPumpPlugin(plugin: EopatchPumpPlugin): PluginBase abstract fun bindEopatchPumpPlugin(plugin: EopatchPumpPlugin): PluginBase
@Binds
@PumpDriver
@IntoMap
@IntKey(160)
abstract fun bindMedtrumPlugin(plugin: MedtrumPlugin): PluginBase
@Binds @Binds
@AllConfigs @AllConfigs
@IntoMap @IntoMap

View file

@ -59,7 +59,7 @@ buildscript {
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:8.1.0' classpath 'com.android.tools.build:gradle:8.1.0'
classpath 'com.google.gms:google-services:4.3.15' classpath 'com.google.gms:google-services:4.3.15'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.7' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.8'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
@ -75,7 +75,7 @@ buildscript {
plugins { plugins {
// Test Gradle build, keep disabled under normal circumstances // Test Gradle build, keep disabled under normal circumstances
// id "com.osacky.doctor" version "0.8.1" // id "com.osacky.doctor" version "0.8.1"
id "org.jlleitschuh.gradle.ktlint" version "11.5.0" id "org.jlleitschuh.gradle.ktlint" version "11.5.1"
// Aggregates and/or logs Jacoco test coverage to the Gradle build log // Aggregates and/or logs Jacoco test coverage to the Gradle build log
id 'org.barfuin.gradle.jacocolog' version '3.1.0' id 'org.barfuin.gradle.jacocolog' version '3.1.0'
id 'org.jetbrains.kotlin.android' version "$kotlin_version" apply false id 'org.jetbrains.kotlin.android' version "$kotlin_version" apply false

View file

@ -131,9 +131,12 @@ open class Notification {
const val IDENTIFICATION_NOT_SET = 77 const val IDENTIFICATION_NOT_SET = 77
const val PERMISSION_BT = 78 const val PERMISSION_BT = 78
const val EOELOW_PATCH_ALERTS = 79 const val EOELOW_PATCH_ALERTS = 79
const val COMBO_PUMP_SUSPENDED = 80 const val PUMP_SUSPENDED = 80
const val COMBO_UNKNOWN_TBR = 81 const val COMBO_UNKNOWN_TBR = 81
const val BLUETOOTH_NOT_ENABLED = 82 const val BLUETOOTH_NOT_ENABLED = 82
const val PATCH_NOT_ACTIVE = 83
const val PUMP_SETTINGS_FAILED = 84
const val PUMP_TIMEZONE_UPDATE_FAILED = 85
const val USER_MESSAGE = 1000 const val USER_MESSAGE = 1000

View file

@ -0,0 +1,13 @@
package info.nightscout.interfaces.pump
/**
* Functionality supported by Medtrum* pumps only
*/
interface Medtrum {
fun loadEvents(): PumpEnactResult // events history to build treatments from
fun setUserOptions(): PumpEnactResult // set user settings
fun clearAlarms(): PumpEnactResult // clear alarms
fun deactivate(): PumpEnactResult // deactivate patch
fun updateTime(): PumpEnactResult // update time
}

View file

@ -2,6 +2,7 @@ package info.nightscout.interfaces.pump.defs
enum class ManufacturerType(val description: String) { enum class ManufacturerType(val description: String) {
AAPS("AAPS"), AAPS("AAPS"),
Medtrum("Medtrum"),
Medtronic("Medtronic"), Medtronic("Medtronic"),
Sooil("SOOIL"), Sooil("SOOIL"),
Tandem("Tandem"), Tandem("Tandem"),

View file

@ -26,6 +26,7 @@ enum class PumpCapability {
YpsomedCapabilities(arrayOf(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, ReplaceBattery, TDD, ManualTDDLoad)), // BasalRates (separately grouped) YpsomedCapabilities(arrayOf(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, ReplaceBattery, TDD, ManualTDDLoad)), // BasalRates (separately grouped)
DiaconnCapabilities(arrayOf(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, ReplaceBattery, TDD, ManualTDDLoad)), // DiaconnCapabilities(arrayOf(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, ReplaceBattery, TDD, ManualTDDLoad)), //
EopatchCapabilities(arrayOf(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, BasalRate30min)), EopatchCapabilities(arrayOf(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, BasalRate30min)),
MedtrumCapabilities(arrayOf(Bolus, TempBasal, BasalProfileSet, BasalRate30min, TDD)), // Technically the pump supports ExtendedBolus, but not implemented (yet)
BasalRate_Duration15minAllowed, BasalRate_Duration15minAllowed,
BasalRate_Duration30minAllowed, BasalRate_Duration30minAllowed,
BasalRate_Duration15and30minAllowed(arrayOf(BasalRate_Duration15minAllowed, BasalRate_Duration30minAllowed)), BasalRate_Duration15and30minAllowed(arrayOf(BasalRate_Duration15minAllowed, BasalRate_Duration30minAllowed)),

View file

@ -391,7 +391,29 @@ enum class PumpType {
isPatchPump = true, isPatchPump = true,
maxReservoirReading = 50, maxReservoirReading = 50,
source = Source.EOPatch2 source = Source.EOPatch2
); ),
//Medtrum Nano Pump
MEDTRUM_NANO(
description = "Medtrum Nano",
manufacturer = ManufacturerType.Medtrum,
model = "Nano",
bolusSize = 0.05,
specialBolusSize = null,
extendedBolusSettings = DoseSettings(0.05, 30, 8 * 60, 0.05, 30.0),
pumpTempBasalType = PumpTempBasalType.Absolute,
tbrSettings = DoseSettings(0.05, 30, 12 * 60, 0.0, 25.0),
specialBasalDurations = PumpCapability.BasalRate_Duration30minAllowed,
baseBasalMinValue = 0.05,
baseBasalMaxValue = 25.0,
baseBasalStep = 0.05,
baseBasalSpecialSteps = null,
pumpCapability = PumpCapability.MedtrumCapabilities,
isPatchPump = true,
maxReservoirReading = 400,
source = Source.Medtrum
),
MEDTRUM_UNTESTED(description = "Medtrum untested", model = "untested", parent = MEDTRUM_NANO);
val description: String val description: String
var manufacturer: ManufacturerType? = null var manufacturer: ManufacturerType? = null
@ -458,6 +480,7 @@ enum class PumpType {
OmnipodEros, OmnipodEros,
OmnipodDash, OmnipodDash,
EOPatch2, EOPatch2,
Medtrum,
MDI, MDI,
VirtualPump, VirtualPump,
Unknown Unknown

View file

@ -23,11 +23,14 @@ abstract class Command(
BASAL_PROFILE, BASAL_PROFILE,
READSTATUS, READSTATUS,
LOAD_HISTORY, // TDDs and so far only Dana specific LOAD_HISTORY, // TDDs and so far only Dana specific
LOAD_EVENTS, // so far only Dana specific LOAD_EVENTS,
LOAD_TDD, LOAD_TDD,
SET_USER_SETTINGS, // so far only Dana specific, SET_USER_SETTINGS, // so far only Dana specific,
START_PUMP, START_PUMP,
STOP_PUMP, STOP_PUMP,
CLEAR_ALARMS, // so far only Medtrum specific
DEACTIVATE, // so far only Medtrum specific
UPDATE_TIME, // so far only Medtrum specific
INSIGHT_SET_TBR_OVER_ALARM, // insight only INSIGHT_SET_TBR_OVER_ALARM, // insight only
CUSTOM_COMMAND CUSTOM_COMMAND
} }

View file

@ -31,6 +31,9 @@ interface CommandQueue {
fun setUserOptions(callback: Callback?): Boolean fun setUserOptions(callback: Callback?): Boolean
fun loadTDDs(callback: Callback?): Boolean fun loadTDDs(callback: Callback?): Boolean
fun loadEvents(callback: Callback?): Boolean fun loadEvents(callback: Callback?): Boolean
fun clearAlarms(callback: Callback?): Boolean
fun deactivate(callback: Callback?): Boolean
fun updateTime(callback: Callback?): Boolean
fun customCommand(customCommand: CustomCommand, callback: Callback?): Boolean fun customCommand(customCommand: CustomCommand, callback: Callback?): Boolean
fun isCustomCommandRunning(customCommandType: Class<out CustomCommand>): Boolean fun isCustomCommandRunning(customCommandType: Class<out CustomCommand>): Boolean
fun isCustomCommandInQueue(customCommandType: Class<out CustomCommand>): Boolean fun isCustomCommandInQueue(customCommandType: Class<out CustomCommand>): Boolean

View file

@ -21,22 +21,23 @@ interface DataSyncSelector {
val value: Any val value: Any
val id: Long val id: Long
var confirmed: Boolean
} }
data class PairTemporaryTarget(override val value: TemporaryTarget, override val id: Long) : DataPair data class PairTemporaryTarget(override val value: TemporaryTarget, override val id: Long, override var confirmed: Boolean = false) : DataPair
data class PairGlucoseValue(override val value: GlucoseValue, override val id: Long) : DataPair data class PairGlucoseValue(override val value: GlucoseValue, override val id: Long, override var confirmed: Boolean = false) : DataPair
data class PairTherapyEvent(override val value: TherapyEvent, override val id: Long) : DataPair data class PairTherapyEvent(override val value: TherapyEvent, override val id: Long, override var confirmed: Boolean = false) : DataPair
data class PairFood(override val value: Food, override val id: Long) : DataPair data class PairFood(override val value: Food, override val id: Long, override var confirmed: Boolean = false) : DataPair
data class PairBolus(override val value: Bolus, override val id: Long) : DataPair data class PairBolus(override val value: Bolus, override val id: Long, override var confirmed: Boolean = false) : DataPair
data class PairCarbs(override val value: Carbs, override val id: Long) : DataPair data class PairCarbs(override val value: Carbs, override val id: Long, override var confirmed: Boolean = false) : DataPair
data class PairBolusCalculatorResult(override val value: BolusCalculatorResult, override val id: Long) : DataPair data class PairBolusCalculatorResult(override val value: BolusCalculatorResult, override val id: Long, override var confirmed: Boolean = false) : DataPair
data class PairTemporaryBasal(override val value: TemporaryBasal, override val id: Long) : DataPair data class PairTemporaryBasal(override val value: TemporaryBasal, override val id: Long, override var confirmed: Boolean = false) : DataPair
data class PairExtendedBolus(override val value: ExtendedBolus, override val id: Long) : DataPair data class PairExtendedBolus(override val value: ExtendedBolus, override val id: Long, override var confirmed: Boolean = false) : DataPair
data class PairProfileSwitch(override val value: ProfileSwitch, override val id: Long) : DataPair data class PairProfileSwitch(override val value: ProfileSwitch, override val id: Long, override var confirmed: Boolean = false) : DataPair
data class PairEffectiveProfileSwitch(override val value: EffectiveProfileSwitch, override val id: Long) : DataPair data class PairEffectiveProfileSwitch(override val value: EffectiveProfileSwitch, override val id: Long, override var confirmed: Boolean = false) : DataPair
data class PairOfflineEvent(override val value: OfflineEvent, override val id: Long) : DataPair data class PairOfflineEvent(override val value: OfflineEvent, override val id: Long, override var confirmed: Boolean = false) : DataPair
data class PairProfileStore(override val value: JSONObject, override val id: Long) : DataPair data class PairProfileStore(override val value: JSONObject, override val id: Long, override var confirmed: Boolean = false) : DataPair
data class PairDeviceStatus(override val value: DeviceStatus, override val id: Long) : DataPair data class PairDeviceStatus(override val value: DeviceStatus, override val id: Long, override var confirmed: Boolean = false) : DataPair
fun queueSize(): Long fun queueSize(): Long

View file

@ -1,46 +0,0 @@
package info.nightscout.interfaces.sync
interface DataSyncSelectorV1 : DataSyncSelector {
fun confirmLastBolusIdIfGreater(lastSynced: Long)
suspend fun processChangedBoluses()
fun confirmLastCarbsIdIfGreater(lastSynced: Long)
suspend fun processChangedCarbs()
fun confirmLastBolusCalculatorResultsIdIfGreater(lastSynced: Long)
suspend fun processChangedBolusCalculatorResults()
fun confirmLastTempTargetsIdIfGreater(lastSynced: Long)
suspend fun processChangedTempTargets()
fun confirmLastGlucoseValueIdIfGreater(lastSynced: Long)
suspend fun processChangedGlucoseValues()
fun confirmLastTherapyEventIdIfGreater(lastSynced: Long)
suspend fun processChangedTherapyEvents()
fun confirmLastFoodIdIfGreater(lastSynced: Long)
suspend fun processChangedFoods()
fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long)
suspend fun processChangedDeviceStatuses()
fun confirmLastTemporaryBasalIdIfGreater(lastSynced: Long)
suspend fun processChangedTemporaryBasals()
fun confirmLastExtendedBolusIdIfGreater(lastSynced: Long)
suspend fun processChangedExtendedBoluses()
fun confirmLastProfileSwitchIdIfGreater(lastSynced: Long)
suspend fun processChangedProfileSwitches()
fun confirmLastEffectiveProfileSwitchIdIfGreater(lastSynced: Long)
suspend fun processChangedEffectiveProfileSwitches()
fun confirmLastOfflineEventIdIfGreater(lastSynced: Long)
suspend fun processChangedOfflineEvents()
fun confirmLastProfileStore(lastSynced: Long)
suspend fun processChangedProfileStore()
}

View file

@ -1,3 +0,0 @@
package info.nightscout.interfaces.sync
interface DataSyncSelectorV3 : DataSyncSelector

View file

@ -59,6 +59,8 @@ fun PumpType.Companion.fromDbPumpType(pt: InterfaceIDs.PumpType): PumpType =
InterfaceIDs.PumpType.USER -> PumpType.USER InterfaceIDs.PumpType.USER -> PumpType.USER
InterfaceIDs.PumpType.DIACONN_G8 -> PumpType.DIACONN_G8 InterfaceIDs.PumpType.DIACONN_G8 -> PumpType.DIACONN_G8
InterfaceIDs.PumpType.EOPATCH2 -> PumpType.EOFLOW_EOPATCH2 InterfaceIDs.PumpType.EOPATCH2 -> PumpType.EOFLOW_EOPATCH2
InterfaceIDs.PumpType.MEDTRUM -> PumpType.MEDTRUM_NANO
InterfaceIDs.PumpType.MEDTRUM_UNTESTED -> PumpType.MEDTRUM_UNTESTED
InterfaceIDs.PumpType.CACHE -> PumpType.CACHE InterfaceIDs.PumpType.CACHE -> PumpType.CACHE
} }
@ -117,5 +119,7 @@ fun PumpType.toDbPumpType(): InterfaceIDs.PumpType =
PumpType.USER -> InterfaceIDs.PumpType.USER PumpType.USER -> InterfaceIDs.PumpType.USER
PumpType.DIACONN_G8 -> InterfaceIDs.PumpType.DIACONN_G8 PumpType.DIACONN_G8 -> InterfaceIDs.PumpType.DIACONN_G8
PumpType.EOFLOW_EOPATCH2 -> InterfaceIDs.PumpType.EOPATCH2 PumpType.EOFLOW_EOPATCH2 -> InterfaceIDs.PumpType.EOPATCH2
PumpType.MEDTRUM_NANO -> InterfaceIDs.PumpType.MEDTRUM
PumpType.MEDTRUM_UNTESTED -> InterfaceIDs.PumpType.MEDTRUM_UNTESTED
PumpType.CACHE -> InterfaceIDs.PumpType.CACHE PumpType.CACHE -> InterfaceIDs.PumpType.CACHE
} }

View file

@ -0,0 +1,71 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<path
android:pathData="M94.38,15.26H34.37c-16.33,0 -29.56,13.3 -29.56,29.71v38.69c0,16.41 13.24,29.71 29.56,29.71h60.02c16.33,0 29.56,-13.3 29.56,-29.71V44.97C123.95,28.56 110.71,15.26 94.38,15.26zM56.94,106.39c-1.27,0 -2.3,-1.03 -2.3,-2.3c0,-1.27 1.03,-2.3 2.3,-2.3c1.27,0 2.3,1.03 2.3,2.3C59.24,105.36 58.21,106.39 56.94,106.39zM56.94,26.83c-1.27,0 -2.3,-1.03 -2.3,-2.3c0,-1.27 1.03,-2.3 2.3,-2.3c1.27,0 2.3,1.03 2.3,2.3C59.24,25.8 58.21,26.83 56.94,26.83zM112.17,73.32V55.3c2.43,2.24 3.95,5.45 3.95,9.01S114.6,71.08 112.17,73.32z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M88.27,99.55c5.2,-0.23 9.45,-0.6 11.93,-1.15c8.84,-1.95 12.24,-6.22 12.24,-13.39v-1.26V44.87v-1.26c0,-7.16 -3.4,-11.44 -12.24,-13.39c-2.47,-0.55 -6.73,-0.92 -11.93,-1.15"
android:fillColor="#E0E0E0"/>
<path
android:pathData="M88.53,29.08c-19.86,-0.79 -52.15,0.13 -58.58,1.57c-8.28,1.85 -12.1,5.19 -13.42,12.92c-0.61,3.54 -0.63,12.31 -0.47,20.75c-0.15,8.44 -0.13,17.2 0.47,20.75c1.32,7.72 5.13,11.07 13.42,12.92c6.43,1.43 38.72,2.36 58.58,1.57"
android:fillColor="#AAAAAA"/>
<path
android:pathData="M88.27,98.82c5.01,-0.22 9.12,-0.59 11.5,-1.12c8.53,-1.91 11.81,-6.09 11.81,-13.11v-1.24V45.27V44.03c0,-7.01 -3.28,-11.2 -11.81,-13.11c-2.38,-0.54 -6.49,-0.9 -11.5,-1.12"
android:fillColor="#EAEAEA"/>
<path
android:pathData="M88.53,29.81c-19.57,-0.77 -51.37,0.13 -57.7,1.53c-8.16,1.81 -11.91,5.09 -13.22,12.65c-0.6,3.47 -0.62,12.05 -0.47,20.32c-0.15,8.27 -0.13,16.85 0.47,20.32c1.3,7.56 5.06,10.84 13.22,12.65c6.33,1.4 38.13,2.31 57.7,1.53"
android:fillColor="#B2B2B2"/>
<path
android:pathData="M88.27,97.88c4.75,-0.22 8.64,-0.57 10.9,-1.09c8.08,-1.86 11.19,-5.93 11.19,-12.75v-1.2V45.78v-1.2c0,-6.82 -3.11,-10.9 -11.19,-12.75c-2.26,-0.52 -6.15,-0.87 -10.9,-1.09"
android:fillColor="#F0F0F0"/>
<path
android:pathData="M88.53,30.75c-19.15,-0.75 -50.27,0.12 -56.47,1.49c-7.99,1.76 -11.66,4.95 -12.93,12.31c-0.58,3.38 -0.6,11.72 -0.46,19.77c-0.15,8.04 -0.13,16.39 0.46,19.77c1.27,7.36 4.95,10.54 12.93,12.31c6.19,1.37 37.32,2.24 56.47,1.49"
android:fillColor="#B9B9B9"/>
<path
android:pathData="M88.27,96.22c4.24,-0.21 7.72,-0.55 9.73,-1.04c7.22,-1.77 9.99,-5.64 9.99,-12.12v-1.14V46.7v-1.14c0,-6.49 -2.78,-10.36 -9.99,-12.12c-2.02,-0.5 -5.49,-0.83 -9.73,-1.04"
android:fillColor="#F4F4F4"/>
<path
android:pathData="M88.53,32.4c-18.73,-0.71 -49.17,0.12 -55.23,1.42c-7.81,1.67 -11.4,4.7 -12.65,11.7c-0.57,3.21 -0.59,11.15 -0.45,18.79c-0.14,7.65 -0.13,15.58 0.45,18.79c1.25,6.99 4.84,10.02 12.65,11.7c6.06,1.3 36.5,2.13 55.23,1.42"
android:fillColor="#BEBEBE"/>
<path
android:pathData="M59.7,75.53l17.16,0l0,-1.29l10.31,0l0,-19.86l-10.31,0l0,-1.3l-17.16,0"
android:strokeWidth="0.4252"
android:strokeColor="#9D9DA0">
<aapt:attr name="android:fillColor">
<gradient
android:startX="59.7"
android:startY="64.31"
android:endX="87.16"
android:endY="64.31"
android:type="linear">
<item android:offset="0" android:color="#FFBBBDBF"/>
<item android:offset="0.12" android:color="#FFB5B7B9"/>
<item android:offset="0.51" android:color="#FFB5B7B9"/>
<item android:offset="0.58" android:color="#FFB5B7B9"/>
<item android:offset="1" android:color="#FF929497"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M90.39,68.37c0,1.84 -1.43,3.32 -3.2,3.32H72.75c-1.77,0 -3.2,-1.49 -3.2,-3.32v-8.12c0,-1.84 1.43,-3.32 3.2,-3.32h14.45c1.77,0 3.2,1.49 3.2,3.32V68.37z"
android:strokeWidth="0.4252"
android:fillColor="#D0D2D3"
android:strokeColor="#A3A3A3"/>
<path
android:pathData="M84.17,74.54c0,4.39 -0.05,4.45 3.63,4.45l0,0c4.39,0 7.95,-3.56 7.95,-7.95v-13.47c0,-4.39 -3.56,-7.95 -7.95,-7.95l0,0c-3.68,0 -3.63,0.1 -3.63,4.49V74.54z"
android:strokeWidth="0.2835"
android:fillColor="#F4F4F4"
android:strokeColor="#C1C1C1"/>
<path
android:pathData="M94.57,67.68c0,0.83 -0.68,1.51 -1.51,1.51l0,0c-0.83,0 -1.51,-0.68 -1.51,-1.51v-6.74c0,-0.83 0.68,-1.51 1.51,-1.51l0,0c0.83,0 1.51,0.68 1.51,1.51V67.68z"
android:strokeWidth="0.2835"
android:fillColor="#F1F1F2"
android:strokeColor="#B2B2B2"/>
<path
android:pathData="M94.29,61.74c0,0.68 -0.55,1.23 -1.23,1.23l0,0c-0.68,0 -1.23,-0.55 -1.23,-1.23l0,0c0,-0.68 0.55,-1.23 1.23,-1.23l0,0C93.74,60.51 94.29,61.06 94.29,61.74L94.29,61.74z"
android:fillColor="#231F20"/>
</vector>

View file

@ -393,6 +393,9 @@
<string name="carbs_g">CARBS %1$d g</string> <string name="carbs_g">CARBS %1$d g</string>
<string name="extended_bolus_u_min">EXTENDED BOLUS %1$.2f U %2$d min</string> <string name="extended_bolus_u_min">EXTENDED BOLUS %1$.2f U %2$d min</string>
<string name="load_events">LOAD EVENTS</string> <string name="load_events">LOAD EVENTS</string>
<string name="clear_alarms">CLEAR ALARMS</string>
<string name="deactivate">DEACTIVATE</string>
<string name="update_time">UPDATE TIME</string>
<string name="load_history">LOAD HISTORY %1$d</string> <string name="load_history">LOAD HISTORY %1$d</string>
<string name="load_tdds">LOAD TDDs</string> <string name="load_tdds">LOAD TDDs</string>
<string name="set_profile">SET PROFILE</string> <string name="set_profile">SET PROFILE</string>

View file

@ -21,7 +21,7 @@ dependencies {
implementation project(':app-wear-shared:shared') implementation project(':app-wear-shared:shared')
//Firebase //Firebase
api platform('com.google.firebase:firebase-bom:32.2.0') api platform('com.google.firebase:firebase-bom:32.2.2')
api "com.google.firebase:firebase-analytics-ktx" api "com.google.firebase:firebase-analytics-ktx"
api "com.google.firebase:firebase-crashlytics-ktx" api "com.google.firebase:firebase-crashlytics-ktx"
// StatsActivity not in use now // StatsActivity not in use now

View file

@ -71,6 +71,10 @@ files:
translation: /pump/danars/src/main/res/values-%android_code%/strings.xml translation: /pump/danars/src/main/res/values-%android_code%/strings.xml
- source: /pump/medtronic/src/main/res/values/strings.xml - source: /pump/medtronic/src/main/res/values/strings.xml
translation: /pump/medtronic/src/main/res/values-%android_code%/strings.xml translation: /pump/medtronic/src/main/res/values-%android_code%/strings.xml
- source: /pump/medtrum/src/main/res/values/strings.xml
translation: /pump/medtrum/src/main/res/values-%android_code%/strings.xml
- source: /pump/medtrum/src/main/res/values/arrays.xml
translation: /pump/medtrum/src/main/res/values-%android_code%/arrays.xml
- source: /pump/omnipod-common/src/main/res/values/strings.xml - source: /pump/omnipod-common/src/main/res/values/strings.xml
translation: /pump/omnipod-common/src/main/res/values-%android_code%/strings.xml translation: /pump/omnipod-common/src/main/res/values-%android_code%/strings.xml
- source: /pump/omnipod-dash/src/main/res/values/strings.xml - source: /pump/omnipod-dash/src/main/res/values/strings.xml

View file

@ -5,6 +5,7 @@ import androidx.room.Entity
import androidx.room.Index import androidx.room.Index
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import info.nightscout.database.entities.embedments.InterfaceIDs import info.nightscout.database.entities.embedments.InterfaceIDs
import info.nightscout.database.entities.interfaces.DBEntry
import info.nightscout.database.entities.interfaces.DBEntryWithTime import info.nightscout.database.entities.interfaces.DBEntryWithTime
import java.util.TimeZone import java.util.TimeZone
@ -17,7 +18,7 @@ import java.util.TimeZone
]) ])
data class DeviceStatus( data class DeviceStatus(
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
var id: Long = 0, override var id: Long = 0,
@Embedded @Embedded
var interfaceIDs_backing: InterfaceIDs? = null, var interfaceIDs_backing: InterfaceIDs? = null,
override var timestamp: Long, override var timestamp: Long,
@ -31,7 +32,7 @@ data class DeviceStatus(
var isCharging: Boolean?, var isCharging: Boolean?,
var configuration: String? = null var configuration: String? = null
) : DBEntryWithTime { ) : DBEntry, DBEntryWithTime {
var interfaceIDs: InterfaceIDs var interfaceIDs: InterfaceIDs
get() { get() {

View file

@ -43,6 +43,8 @@ data class InterfaceIDs(
MDI, MDI,
DIACONN_G8, DIACONN_G8,
EOPATCH2, EOPATCH2,
MEDTRUM,
MEDTRUM_UNTESTED,
USER, USER,
CACHE; CACHE;

View file

@ -254,6 +254,7 @@ import kotlin.math.roundToInt
fun insert(word: UserEntry) { fun insert(word: UserEntry) {
database.userEntryDao.insert(word) database.userEntryDao.insert(word)
changeSubject.onNext(mutableListOf(word)) // Not TraceableDao
} }
// PROFILE SWITCH // PROFILE SWITCH
@ -712,8 +713,10 @@ import kotlin.math.roundToInt
database.bolusCalculatorResultDao.getLastId() database.bolusCalculatorResultDao.getLastId()
// DEVICE STATUS // DEVICE STATUS
fun insert(deviceStatus: DeviceStatus): Long = fun insert(deviceStatus: DeviceStatus) {
database.deviceStatusDao.insert(deviceStatus) database.deviceStatusDao.insert(deviceStatus)
changeSubject.onNext(mutableListOf(deviceStatus)) // Not TraceableDao
}
/* /*
* returns a Pair of the next entity to sync and the ID of the "update". * returns a Pair of the next entity to sync and the ID of the "update".
@ -727,10 +730,6 @@ import kotlin.math.roundToInt
database.deviceStatusDao.getNextModifiedOrNewAfter(id) database.deviceStatusDao.getNextModifiedOrNewAfter(id)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
fun getModifiedDeviceStatusDataFromId(lastId: Long): Single<List<DeviceStatus>> =
database.deviceStatusDao.getModifiedFrom(lastId)
.subscribeOn(Schedulers.io())
fun getLastDeviceStatusId(): Long? = fun getLastDeviceStatusId(): Long? =
database.deviceStatusDao.getLastId() database.deviceStatusDao.getLastId()

View file

@ -3,6 +3,7 @@ package info.nightscout.implementation.db
import android.content.Context import android.content.Context
import info.nightscout.database.entities.Bolus import info.nightscout.database.entities.Bolus
import info.nightscout.database.entities.Carbs import info.nightscout.database.entities.Carbs
import info.nightscout.database.entities.DeviceStatus
import info.nightscout.database.entities.EffectiveProfileSwitch import info.nightscout.database.entities.EffectiveProfileSwitch
import info.nightscout.database.entities.ExtendedBolus import info.nightscout.database.entities.ExtendedBolus
import info.nightscout.database.entities.Food import info.nightscout.database.entities.Food
@ -15,6 +16,7 @@ import info.nightscout.database.entities.TherapyEvent
import info.nightscout.database.impl.AppRepository import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventDeviceStatusChange
import info.nightscout.rx.events.EventEffectiveProfileSwitchChanged import info.nightscout.rx.events.EventEffectiveProfileSwitchChanged
import info.nightscout.rx.events.EventExtendedBolusChange import info.nightscout.rx.events.EventExtendedBolusChange
import info.nightscout.rx.events.EventFoodDatabaseChanged import info.nightscout.rx.events.EventFoodDatabaseChanged
@ -109,5 +111,9 @@ class CompatDBHelper @Inject constructor(
aapsLogger.debug(LTag.DATABASE, "Firing EventOfflineChange $oe") aapsLogger.debug(LTag.DATABASE, "Firing EventOfflineChange $oe")
rxBus.send(EventOfflineChange()) rxBus.send(EventOfflineChange())
} }
it.filterIsInstance<DeviceStatus>().firstOrNull()?.let { ds ->
aapsLogger.debug(LTag.DATABASE, "Firing EventDeviceStatusChange $ds")
rxBus.send(EventDeviceStatusChange())
}
} }
} }

View file

@ -14,12 +14,15 @@ import info.nightscout.implementation.queue.commands.CommandTempBasalPercent
import info.nightscout.implementation.queue.commands.CommandBolus import info.nightscout.implementation.queue.commands.CommandBolus
import info.nightscout.implementation.queue.commands.CommandCancelExtendedBolus import info.nightscout.implementation.queue.commands.CommandCancelExtendedBolus
import info.nightscout.implementation.queue.commands.CommandCancelTempBasal import info.nightscout.implementation.queue.commands.CommandCancelTempBasal
import info.nightscout.implementation.queue.commands.CommandClearAlarms
import info.nightscout.implementation.queue.commands.CommandCustomCommand import info.nightscout.implementation.queue.commands.CommandCustomCommand
import info.nightscout.implementation.queue.commands.CommandDeactivate
import info.nightscout.implementation.queue.commands.CommandExtendedBolus import info.nightscout.implementation.queue.commands.CommandExtendedBolus
import info.nightscout.implementation.queue.commands.CommandInsightSetTBROverNotification import info.nightscout.implementation.queue.commands.CommandInsightSetTBROverNotification
import info.nightscout.implementation.queue.commands.CommandLoadEvents import info.nightscout.implementation.queue.commands.CommandLoadEvents
import info.nightscout.implementation.queue.commands.CommandLoadHistory import info.nightscout.implementation.queue.commands.CommandLoadHistory
import info.nightscout.implementation.queue.commands.CommandLoadTDDs import info.nightscout.implementation.queue.commands.CommandLoadTDDs
import info.nightscout.implementation.queue.commands.CommandUpdateTime
@Module @Module
@Suppress("unused") @Suppress("unused")
@ -32,6 +35,9 @@ abstract class CommandQueueModule {
@ContributesAndroidInjector abstract fun commandExtendedBolusInjector(): CommandExtendedBolus @ContributesAndroidInjector abstract fun commandExtendedBolusInjector(): CommandExtendedBolus
@ContributesAndroidInjector abstract fun commandInsightSetTBROverNotificationInjector(): CommandInsightSetTBROverNotification @ContributesAndroidInjector abstract fun commandInsightSetTBROverNotificationInjector(): CommandInsightSetTBROverNotification
@ContributesAndroidInjector abstract fun commandLoadEventsInjector(): CommandLoadEvents @ContributesAndroidInjector abstract fun commandLoadEventsInjector(): CommandLoadEvents
@ContributesAndroidInjector abstract fun commandClearAlarmsInjector(): CommandClearAlarms
@ContributesAndroidInjector abstract fun commandDeactivateInjector(): CommandDeactivate
@ContributesAndroidInjector abstract fun commandUpdateTimeInjector(): CommandUpdateTime
@ContributesAndroidInjector abstract fun commandLoadHistoryInjector(): CommandLoadHistory @ContributesAndroidInjector abstract fun commandLoadHistoryInjector(): CommandLoadHistory
@ContributesAndroidInjector abstract fun commandLoadTDDsInjector(): CommandLoadTDDs @ContributesAndroidInjector abstract fun commandLoadTDDsInjector(): CommandLoadTDDs
@ContributesAndroidInjector abstract fun commandReadStatusInjector(): CommandReadStatus @ContributesAndroidInjector abstract fun commandReadStatusInjector(): CommandReadStatus

View file

@ -1,11 +1,15 @@
package info.nightscout.implementation.pump package info.nightscout.implementation.pump
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import info.nightscout.androidaps.annotations.OpenForTesting import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.implementation.R
import info.nightscout.interfaces.pump.DetailedBolusInfo import info.nightscout.interfaces.pump.DetailedBolusInfo
import info.nightscout.interfaces.pump.DetailedBolusInfoStorage import info.nightscout.interfaces.pump.DetailedBolusInfoStorage
import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.rx.logging.LTag import info.nightscout.rx.logging.LTag
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.utils.T import info.nightscout.shared.utils.T
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -14,10 +18,12 @@ import kotlin.math.abs
@OpenForTesting @OpenForTesting
@Singleton @Singleton
class DetailedBolusInfoStorageImpl @Inject constructor( class DetailedBolusInfoStorageImpl @Inject constructor(
val aapsLogger: AAPSLogger val aapsLogger: AAPSLogger,
val sp: SP,
val rh: ResourceHelper
) : DetailedBolusInfoStorage { ) : DetailedBolusInfoStorage {
val store = ArrayList<DetailedBolusInfo>() val store = loadStore()
fun DetailedBolusInfo.toJsonString(): String = Gson().toJson(this) fun DetailedBolusInfo.toJsonString(): String = Gson().toJson(this)
@ -25,6 +31,7 @@ class DetailedBolusInfoStorageImpl @Inject constructor(
override fun add(detailedBolusInfo: DetailedBolusInfo) { override fun add(detailedBolusInfo: DetailedBolusInfo) {
aapsLogger.debug("Stored bolus info: ${detailedBolusInfo.toJsonString()}") aapsLogger.debug("Stored bolus info: ${detailedBolusInfo.toJsonString()}")
store.add(detailedBolusInfo) store.add(detailedBolusInfo)
saveStore()
} }
@Synchronized @Synchronized
@ -36,6 +43,7 @@ class DetailedBolusInfoStorageImpl @Inject constructor(
if (bolusTime > d.timestamp - T.mins(1).msecs() && bolusTime < d.timestamp + T.mins(1).msecs() && abs(store[i].insulin - bolus) < 0.01) { if (bolusTime > d.timestamp - T.mins(1).msecs() && bolusTime < d.timestamp + T.mins(1).msecs() && abs(store[i].insulin - bolus) < 0.01) {
aapsLogger.debug(LTag.PUMP, "Using & removing bolus info for time $bolusTime: ${store[i]}") aapsLogger.debug(LTag.PUMP, "Using & removing bolus info for time $bolusTime: ${store[i]}")
store.removeAt(i) store.removeAt(i)
saveStore()
return d return d
} }
} }
@ -45,6 +53,7 @@ class DetailedBolusInfoStorageImpl @Inject constructor(
if (bolusTime > d.timestamp - T.mins(1).msecs() && bolusTime < d.timestamp + T.mins(1).msecs() && bolus <= store[i].insulin + 0.01) { if (bolusTime > d.timestamp - T.mins(1).msecs() && bolusTime < d.timestamp + T.mins(1).msecs() && bolus <= store[i].insulin + 0.01) {
aapsLogger.debug(LTag.PUMP, "Using TIME-ONLY & removing bolus info for time $bolusTime: ${store[i]}") aapsLogger.debug(LTag.PUMP, "Using TIME-ONLY & removing bolus info for time $bolusTime: ${store[i]}")
store.removeAt(i) store.removeAt(i)
saveStore()
return d return d
} }
} }
@ -61,4 +70,24 @@ class DetailedBolusInfoStorageImpl @Inject constructor(
aapsLogger.debug(LTag.PUMP, "Bolus info not found for time $bolusTime") aapsLogger.debug(LTag.PUMP, "Bolus info not found for time $bolusTime")
return null return null
} }
private fun saveStore() {
var lastTwoEntries = store
// Only save last two entries, to avoid too much data in preferences
if (store.size > 2) {
lastTwoEntries = ArrayList(store.subList(store.size - 2, store.size))
}
val jsonString = Gson().toJson(lastTwoEntries)
sp.putString(rh.gs(R.string.key_bolus_storage), jsonString)
}
private fun loadStore(): ArrayList<DetailedBolusInfo> {
val jsonString = sp.getString(rh.gs(R.string.key_bolus_storage), "")
return if (jsonString.isNotEmpty()) {
val type = object : TypeToken<List<DetailedBolusInfo>>() {}.type
Gson().fromJson(jsonString, type)
} else {
ArrayList()
}
}
} }

View file

@ -22,7 +22,9 @@ import info.nightscout.implementation.R
import info.nightscout.implementation.queue.commands.CommandBolus import info.nightscout.implementation.queue.commands.CommandBolus
import info.nightscout.implementation.queue.commands.CommandCancelExtendedBolus import info.nightscout.implementation.queue.commands.CommandCancelExtendedBolus
import info.nightscout.implementation.queue.commands.CommandCancelTempBasal import info.nightscout.implementation.queue.commands.CommandCancelTempBasal
import info.nightscout.implementation.queue.commands.CommandClearAlarms
import info.nightscout.implementation.queue.commands.CommandCustomCommand import info.nightscout.implementation.queue.commands.CommandCustomCommand
import info.nightscout.implementation.queue.commands.CommandDeactivate
import info.nightscout.implementation.queue.commands.CommandExtendedBolus import info.nightscout.implementation.queue.commands.CommandExtendedBolus
import info.nightscout.implementation.queue.commands.CommandInsightSetTBROverNotification import info.nightscout.implementation.queue.commands.CommandInsightSetTBROverNotification
import info.nightscout.implementation.queue.commands.CommandLoadEvents import info.nightscout.implementation.queue.commands.CommandLoadEvents
@ -36,6 +38,7 @@ import info.nightscout.implementation.queue.commands.CommandStartPump
import info.nightscout.implementation.queue.commands.CommandStopPump import info.nightscout.implementation.queue.commands.CommandStopPump
import info.nightscout.implementation.queue.commands.CommandTempBasalAbsolute import info.nightscout.implementation.queue.commands.CommandTempBasalAbsolute
import info.nightscout.implementation.queue.commands.CommandTempBasalPercent import info.nightscout.implementation.queue.commands.CommandTempBasalPercent
import info.nightscout.implementation.queue.commands.CommandUpdateTime
import info.nightscout.interfaces.AndroidPermission import info.nightscout.interfaces.AndroidPermission
import info.nightscout.interfaces.Config import info.nightscout.interfaces.Config
import info.nightscout.interfaces.constraints.Constraint import info.nightscout.interfaces.constraints.Constraint
@ -535,6 +538,46 @@ class CommandQueueImplementation @Inject constructor(
return true return true
} }
// returns true if command is queued
override fun clearAlarms(callback: Callback?): Boolean {
if (isRunning(CommandType.CLEAR_ALARMS)) {
callback?.result(executingNowError())?.run()
return false
}
// remove all unfinished
removeAll(CommandType.CLEAR_ALARMS)
// add new command to queue
add(CommandClearAlarms(injector, callback))
notifyAboutNewCommand()
return true
}
override fun deactivate(callback: Callback?): Boolean {
if (isRunning(CommandType.DEACTIVATE)) {
callback?.result(executingNowError())?.run()
return false
}
// remove all unfinished
removeAll(CommandType.DEACTIVATE)
// add new command to queue
add(CommandDeactivate(injector, callback))
notifyAboutNewCommand()
return true
}
override fun updateTime(callback: Callback?): Boolean {
if (isRunning(CommandType.UPDATE_TIME)) {
callback?.result(executingNowError())?.run()
return false
}
// remove all unfinished
removeAll(CommandType.UPDATE_TIME)
// add new command to queue
add(CommandUpdateTime(injector, callback))
notifyAboutNewCommand()
return true
}
override fun customCommand(customCommand: CustomCommand, callback: Callback?): Boolean { override fun customCommand(customCommand: CustomCommand, callback: Callback?): Boolean {
if (isCustomCommandInQueue(customCommand.javaClass)) { if (isCustomCommandInQueue(customCommand.javaClass)) {
callback?.result(executingNowError())?.run() callback?.result(executingNowError())?.run()

View file

@ -0,0 +1,39 @@
package info.nightscout.implementation.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.pump.Dana
import info.nightscout.interfaces.pump.Diaconn
import info.nightscout.interfaces.pump.Medtrum
import info.nightscout.interfaces.pump.PumpEnactResult
import info.nightscout.interfaces.queue.Callback
import info.nightscout.interfaces.queue.Command
import info.nightscout.rx.logging.LTag
import javax.inject.Inject
class CommandClearAlarms(
injector: HasAndroidInjector,
callback: Callback?
) : Command(injector, CommandType.CLEAR_ALARMS, callback) {
@Inject lateinit var activePlugin: ActivePlugin
override fun execute() {
val pump = activePlugin.activePump
if (pump is Medtrum) {
val medtrumPump = pump as Medtrum
val r = medtrumPump.clearAlarms()
aapsLogger.debug(LTag.PUMPQUEUE, "Result success: ${r.success} enacted: ${r.enacted}")
callback?.result(r)?.run()
}
}
override fun status(): String = rh.gs(info.nightscout.core.ui.R.string.clear_alarms)
override fun log(): String = "CLEAR ALARMS"
override fun cancel() {
aapsLogger.debug(LTag.PUMPQUEUE, "Result cancel")
callback?.result(PumpEnactResult(injector).success(false).comment(info.nightscout.core.ui.R.string.connectiontimedout))?.run()
}
}

View file

@ -0,0 +1,39 @@
package info.nightscout.implementation.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.pump.Dana
import info.nightscout.interfaces.pump.Diaconn
import info.nightscout.interfaces.pump.Medtrum
import info.nightscout.interfaces.pump.PumpEnactResult
import info.nightscout.interfaces.queue.Callback
import info.nightscout.interfaces.queue.Command
import info.nightscout.rx.logging.LTag
import javax.inject.Inject
class CommandDeactivate(
injector: HasAndroidInjector,
callback: Callback?
) : Command(injector, CommandType.DEACTIVATE, callback) {
@Inject lateinit var activePlugin: ActivePlugin
override fun execute() {
val pump = activePlugin.activePump
if (pump is Medtrum) {
val medtrumPump = pump as Medtrum
val r = medtrumPump.deactivate()
aapsLogger.debug(LTag.PUMPQUEUE, "Result success: ${r.success} enacted: ${r.enacted}")
callback?.result(r)?.run()
}
}
override fun status(): String = rh.gs(info.nightscout.core.ui.R.string.deactivate)
override fun log(): String = "DEACTIVATE"
override fun cancel() {
aapsLogger.debug(LTag.PUMPQUEUE, "Result cancel")
callback?.result(PumpEnactResult(injector).success(false).comment(info.nightscout.core.ui.R.string.connectiontimedout))?.run()
}
}

View file

@ -4,6 +4,7 @@ import dagger.android.HasAndroidInjector
import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.pump.Dana import info.nightscout.interfaces.pump.Dana
import info.nightscout.interfaces.pump.Diaconn import info.nightscout.interfaces.pump.Diaconn
import info.nightscout.interfaces.pump.Medtrum
import info.nightscout.interfaces.pump.PumpEnactResult import info.nightscout.interfaces.pump.PumpEnactResult
import info.nightscout.interfaces.queue.Callback import info.nightscout.interfaces.queue.Callback
import info.nightscout.interfaces.queue.Command import info.nightscout.interfaces.queue.Command
@ -32,6 +33,13 @@ class CommandLoadEvents(
aapsLogger.debug(LTag.PUMPQUEUE, "Result success: ${r.success} enacted: ${r.enacted}") aapsLogger.debug(LTag.PUMPQUEUE, "Result success: ${r.success} enacted: ${r.enacted}")
callback?.result(r)?.run() callback?.result(r)?.run()
} }
if (pump is Medtrum) {
val medtrumPump = pump as Medtrum
val r = medtrumPump.loadEvents()
aapsLogger.debug(LTag.PUMPQUEUE, "Result success: ${r.success} enacted: ${r.enacted}")
callback?.result(r)?.run()
}
} }
override fun status(): String = rh.gs(info.nightscout.core.ui.R.string.load_events) override fun status(): String = rh.gs(info.nightscout.core.ui.R.string.load_events)

View file

@ -4,6 +4,7 @@ import dagger.android.HasAndroidInjector
import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.pump.Dana import info.nightscout.interfaces.pump.Dana
import info.nightscout.interfaces.pump.Diaconn import info.nightscout.interfaces.pump.Diaconn
import info.nightscout.interfaces.pump.Medtrum
import info.nightscout.interfaces.pump.PumpEnactResult import info.nightscout.interfaces.pump.PumpEnactResult
import info.nightscout.interfaces.queue.Callback import info.nightscout.interfaces.queue.Callback
import info.nightscout.interfaces.queue.Command import info.nightscout.interfaces.queue.Command
@ -30,6 +31,12 @@ class CommandSetUserSettings(
aapsLogger.debug(LTag.PUMPQUEUE, "Result success: ${r.success} enacted: ${r.enacted}") aapsLogger.debug(LTag.PUMPQUEUE, "Result success: ${r.success} enacted: ${r.enacted}")
callback?.result(r)?.run() callback?.result(r)?.run()
} }
if (pump is Medtrum) {
val r = pump.setUserOptions()
aapsLogger.debug(LTag.PUMPQUEUE, "Result success: ${r.success} enacted: ${r.enacted}")
callback?.result(r)?.run()
}
} }
override fun status(): String = rh.gs(info.nightscout.core.ui.R.string.set_user_settings) override fun status(): String = rh.gs(info.nightscout.core.ui.R.string.set_user_settings)

View file

@ -0,0 +1,39 @@
package info.nightscout.implementation.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.pump.Dana
import info.nightscout.interfaces.pump.Diaconn
import info.nightscout.interfaces.pump.Medtrum
import info.nightscout.interfaces.pump.PumpEnactResult
import info.nightscout.interfaces.queue.Callback
import info.nightscout.interfaces.queue.Command
import info.nightscout.rx.logging.LTag
import javax.inject.Inject
class CommandUpdateTime(
injector: HasAndroidInjector,
callback: Callback?
) : Command(injector, CommandType.UPDATE_TIME, callback) {
@Inject lateinit var activePlugin: ActivePlugin
override fun execute() {
val pump = activePlugin.activePump
if (pump is Medtrum) {
val medtrumPump = pump as Medtrum
val r = medtrumPump.updateTime()
aapsLogger.debug(LTag.PUMPQUEUE, "Result success: ${r.success} enacted: ${r.enacted}")
callback?.result(r)?.run()
}
}
override fun status(): String = rh.gs(info.nightscout.core.ui.R.string.update_time)
override fun log(): String = "UPDATE TIME"
override fun cancel() {
aapsLogger.debug(LTag.PUMPQUEUE, "Result cancel")
callback?.result(PumpEnactResult(injector).success(false).comment(info.nightscout.core.ui.R.string.connectiontimedout))?.run()
}
}

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="key_bolus_storage" translatable="false">key_bolus_storage</string>
<string name="bg_label">BG</string> <string name="bg_label">BG</string>
<string name="executing_right_now">Command is executed right now</string> <string name="executing_right_now">Command is executed right now</string>

View file

@ -1,14 +1,22 @@
package info.nightscout.implementation.pump package info.nightscout.implementation.pump
import info.nightscout.androidaps.TestBase import info.nightscout.androidaps.TestBase
import info.nightscout.implementation.R
import info.nightscout.interfaces.pump.DetailedBolusInfo import info.nightscout.interfaces.pump.DetailedBolusInfo
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull import org.junit.Assert.assertNull
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.mockito.Mock
import org.mockito.Mockito
class DetailedBolusInfoStorageTest : TestBase() { class DetailedBolusInfoStorageTest : TestBase() {
@Mock lateinit var sp: SP
@Mock lateinit var rh: ResourceHelper
private val info1 = DetailedBolusInfo() private val info1 = DetailedBolusInfo()
private val info2 = DetailedBolusInfo() private val info2 = DetailedBolusInfo()
private val info3 = DetailedBolusInfo() private val info3 = DetailedBolusInfo()
@ -26,7 +34,8 @@ class DetailedBolusInfoStorageTest : TestBase() {
@BeforeEach @BeforeEach
fun prepare() { fun prepare() {
detailedBolusInfoStorage = DetailedBolusInfoStorageImpl(aapsLogger) Mockito.`when`(sp.getString(rh.gs(R.string.key_bolus_storage), "")).thenReturn("")
detailedBolusInfoStorage = DetailedBolusInfoStorageImpl(aapsLogger, sp, rh)
} }
private fun setUp() { private fun setUp() {

View file

@ -238,6 +238,19 @@ class CommandQueueImplementationTest : TestBaseWithProfile() {
// add loadEvents // add loadEvents
commandQueue.loadEvents(null) commandQueue.loadEvents(null)
Assertions.assertEquals(4, commandQueue.size()) Assertions.assertEquals(4, commandQueue.size())
// add clearAlarms
commandQueue.clearAlarms(null)
Assertions.assertEquals(5, commandQueue.size())
// add deactivate
commandQueue.deactivate(null)
Assertions.assertEquals(6, commandQueue.size())
// add updateTime
commandQueue.updateTime(null)
Assertions.assertEquals(7, commandQueue.size())
commandQueue.clear() commandQueue.clear()
commandQueue.tempBasalAbsolute(0.0, 30, true, validProfile, PumpSync.TemporaryBasalType.NORMAL, null) commandQueue.tempBasalAbsolute(0.0, 30, true, validProfile, PumpSync.TemporaryBasalType.NORMAL, null)
commandQueue.pickup() commandQueue.pickup()
@ -354,6 +367,54 @@ class CommandQueueImplementationTest : TestBaseWithProfile() {
Assertions.assertEquals(1, commandQueue.size()) Assertions.assertEquals(1, commandQueue.size())
} }
@Test
fun isClearAlarmsCommandInQueue() {
// given
Assertions.assertEquals(0, commandQueue.size())
// when
commandQueue.clearAlarms(null)
// then
Assertions.assertTrue(commandQueue.isLastScheduled(Command.CommandType.CLEAR_ALARMS))
Assertions.assertEquals(1, commandQueue.size())
// next should be ignored
commandQueue.clearAlarms(null)
Assertions.assertEquals(1, commandQueue.size())
}
@Test
fun isDeactivateCommandInQueue() {
// given
Assertions.assertEquals(0, commandQueue.size())
// when
commandQueue.deactivate(null)
// then
Assertions.assertTrue(commandQueue.isLastScheduled(Command.CommandType.DEACTIVATE))
Assertions.assertEquals(1, commandQueue.size())
// next should be ignored
commandQueue.deactivate(null)
Assertions.assertEquals(1, commandQueue.size())
}
@Test
fun isUpdateTimeCommandInQueue() {
// given
Assertions.assertEquals(0, commandQueue.size())
// when
commandQueue.updateTime(null)
// then
Assertions.assertTrue(commandQueue.isLastScheduled(Command.CommandType.UPDATE_TIME))
Assertions.assertEquals(1, commandQueue.size())
// next should be ignored
commandQueue.updateTime(null)
Assertions.assertEquals(1, commandQueue.size())
}
@Test @Test
fun isLoadTDDsCommandInQueue() { fun isLoadTDDsCommandInQueue() {
// given // given

View file

@ -35,6 +35,7 @@ import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.plugin.PluginBase import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.plugin.PluginType import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.profile.ProfileFunction import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.pump.Medtrum
import info.nightscout.interfaces.pump.OmnipodDash import info.nightscout.interfaces.pump.OmnipodDash
import info.nightscout.interfaces.pump.OmnipodEros import info.nightscout.interfaces.pump.OmnipodEros
import info.nightscout.interfaces.queue.CommandQueue import info.nightscout.interfaces.queue.CommandQueue
@ -319,23 +320,24 @@ class SWDefinition @Inject constructor(
.text(R.string.readstatus) .text(R.string.readstatus)
.action { commandQueue.readStatus(rh.gs(info.nightscout.core.ui.R.string.clicked_connect_to_pump), null) } .action { commandQueue.readStatus(rh.gs(info.nightscout.core.ui.R.string.clicked_connect_to_pump), null) }
.visibility { .visibility {
// Hide for Omnipod, because as we don't require a Pod to be paired in the setup wizard, // Hide for Omnipod and Medtrum, because as we don't require a Pod/Patch to be paired in the setup wizard,
// Getting the status might not be possible // Getting the status might not be possible
activePlugin.activePump !is OmnipodEros && activePlugin.activePump !is OmnipodDash activePlugin.activePump !is OmnipodEros && activePlugin.activePump !is OmnipodDash && activePlugin.activePump !is Medtrum
}) })
.add(SWEventListener(injector, EventPumpStatusChanged::class.java) .add(SWEventListener(injector, EventPumpStatusChanged::class.java)
.visibility { activePlugin.activePump !is OmnipodEros && activePlugin.activePump !is OmnipodDash }) .visibility { activePlugin.activePump !is OmnipodEros && activePlugin.activePump !is OmnipodDash && activePlugin.activePump !is Medtrum })
.validator { isPumpInitialized() } .validator { isPumpInitialized() }
private fun isPumpInitialized(): Boolean { private fun isPumpInitialized(): Boolean {
val activePump = activePlugin.activePump val activePump = activePlugin.activePump
// For Omnipod, activating a Pod can be done after setup through the Omnipod fragment // For Omnipod and Medtrum, activating a Pod/Patch can be done after setup through the pump fragment
// For the Eros model, consider the pump initialized when a RL has been configured successfully // For the Eros, consider the pump initialized when a RL has been configured successfully
// For Dash model, consider the pump setup without any extra conditions // For all others, consider the pump setup without any extra conditions
return activePump.isInitialized() return activePump.isInitialized()
|| (activePump is OmnipodEros && activePump.isRileyLinkReady()) || (activePump is OmnipodEros && activePump.isRileyLinkReady())
|| activePump is OmnipodDash || activePump is OmnipodDash
|| activePump is Medtrum
} }
private val screenAps private val screenAps

View file

@ -6,6 +6,7 @@ import info.nightscout.shared.utils.DateUtil
import java.net.DatagramPacket import java.net.DatagramPacket
import java.net.DatagramSocket import java.net.DatagramSocket
import java.net.InetAddress import java.net.InetAddress
import java.security.SecureRandom
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -195,6 +196,6 @@ class SntpClient @Inject constructor(
buffer[offset++] = (fraction shr 16).toByte() buffer[offset++] = (fraction shr 16).toByte()
buffer[offset++] = (fraction shr 8).toByte() buffer[offset++] = (fraction shr 8).toByte()
// low order bits should be random data // low order bits should be random data
buffer[offset] = (Math.random() * 255.0).toInt().toByte() buffer[offset] = SecureRandom().nextInt(256).toByte()
} }
} }

View file

@ -48,10 +48,14 @@ class ThemeSwitcherPlugin @Inject constructor(
} }
fun setThemeMode() { fun setThemeMode() {
val mode = when (sp.getString(info.nightscout.core.utils.R.string.key_use_dark_mode, "dark")) { val mode = try {
sp.getString(R.string.value_dark_theme, "dark") -> MODE_NIGHT_YES when (sp.getString(info.nightscout.core.utils.R.string.key_use_dark_mode, "dark")) {
sp.getString(R.string.value_light_theme, "light") -> MODE_NIGHT_NO sp.getString(R.string.value_dark_theme, "dark") -> MODE_NIGHT_YES
else -> MODE_NIGHT_FOLLOW_SYSTEM sp.getString(R.string.value_light_theme, "light") -> MODE_NIGHT_NO
else -> MODE_NIGHT_FOLLOW_SYSTEM
}
} catch (ignored: Exception) {
MODE_NIGHT_FOLLOW_SYSTEM
} }
AppCompatDelegate.setDefaultNightMode(mode) AppCompatDelegate.setDefaultNightMode(mode)
} }

View file

@ -22,7 +22,7 @@ import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.T import info.nightscout.shared.utils.T
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import java.lang.Math.random import java.security.SecureRandom
import java.util.Calendar import java.util.Calendar
import java.util.GregorianCalendar import java.util.GregorianCalendar
import javax.inject.Inject import javax.inject.Inject
@ -100,7 +100,7 @@ class RandomBgPlugin @Inject constructor(
val cal = GregorianCalendar() val cal = GregorianCalendar()
val currentMinute = cal[Calendar.MINUTE] + (cal[Calendar.HOUR_OF_DAY] % 2) * 60 val currentMinute = cal[Calendar.MINUTE] + (cal[Calendar.HOUR_OF_DAY] % 2) * 60
val bgMgdl = min + ((max - min) + (max - min) * sin(currentMinute / period * 2 * PI)) / 2 + (random() - 0.5) * (max - min) * 0.4 val bgMgdl = min + ((max - min) + (max - min) * sin(currentMinute / period * 2 * PI)) / 2 + (SecureRandom().nextDouble() - 0.5) * (max - min) * 0.4
cal[Calendar.MILLISECOND] = 0 cal[Calendar.MILLISECOND] = 0
cal[Calendar.SECOND] = 0 cal[Calendar.SECOND] = 0

View file

@ -11,12 +11,9 @@ import info.nightscout.interfaces.XDripBroadcast
import info.nightscout.interfaces.nsclient.NSSettingsStatus import info.nightscout.interfaces.nsclient.NSSettingsStatus
import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData
import info.nightscout.interfaces.nsclient.StoreDataForDb import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.sync.DataSyncSelectorV1
import info.nightscout.interfaces.sync.DataSyncSelectorV3
import info.nightscout.interfaces.sync.DataSyncSelectorXdrip import info.nightscout.interfaces.sync.DataSyncSelectorXdrip
import info.nightscout.plugins.sync.nsShared.NSClientFragment import info.nightscout.plugins.sync.nsShared.NSClientFragment
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsclient.DataSyncSelectorV1Impl
import info.nightscout.plugins.sync.nsclient.data.NSSettingsStatusImpl import info.nightscout.plugins.sync.nsclient.data.NSSettingsStatusImpl
import info.nightscout.plugins.sync.nsclient.data.ProcessedDeviceStatusDataImpl import info.nightscout.plugins.sync.nsclient.data.ProcessedDeviceStatusDataImpl
import info.nightscout.plugins.sync.nsclient.services.NSClientService import info.nightscout.plugins.sync.nsclient.services.NSClientService
@ -24,8 +21,14 @@ import info.nightscout.plugins.sync.nsclient.workers.NSClientAddAckWorker
import info.nightscout.plugins.sync.nsclient.workers.NSClientAddUpdateWorker import info.nightscout.plugins.sync.nsclient.workers.NSClientAddUpdateWorker
import info.nightscout.plugins.sync.nsclient.workers.NSClientMbgWorker import info.nightscout.plugins.sync.nsclient.workers.NSClientMbgWorker
import info.nightscout.plugins.sync.nsclient.workers.NSClientUpdateRemoveAckWorker import info.nightscout.plugins.sync.nsclient.workers.NSClientUpdateRemoveAckWorker
import info.nightscout.plugins.sync.nsclientV3.DataSyncSelectorV3Impl import info.nightscout.plugins.sync.nsclientV3.workers.DataSyncWorker
import info.nightscout.plugins.sync.nsclientV3.workers.* import info.nightscout.plugins.sync.nsclientV3.workers.LoadBgWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadDeviceStatusWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadFoodsWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadLastModificationWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadProfileStoreWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadStatusWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadTreatmentsWorker
import info.nightscout.plugins.sync.tidepool.TidepoolFragment import info.nightscout.plugins.sync.tidepool.TidepoolFragment
import info.nightscout.plugins.sync.xdrip.DataSyncSelectorXdripImpl import info.nightscout.plugins.sync.xdrip.DataSyncSelectorXdripImpl
import info.nightscout.plugins.sync.xdrip.XdripFragment import info.nightscout.plugins.sync.xdrip.XdripFragment
@ -75,8 +78,6 @@ abstract class SyncModule {
@Binds fun bindProcessedDeviceStatusData(processedDeviceStatusDataImpl: ProcessedDeviceStatusDataImpl): ProcessedDeviceStatusData @Binds fun bindProcessedDeviceStatusData(processedDeviceStatusDataImpl: ProcessedDeviceStatusDataImpl): ProcessedDeviceStatusData
@Binds fun bindNSSettingsStatus(nsSettingsStatusImpl: NSSettingsStatusImpl): NSSettingsStatus @Binds fun bindNSSettingsStatus(nsSettingsStatusImpl: NSSettingsStatusImpl): NSSettingsStatus
@Binds fun bindDataSyncSelectorV1Interface(dataSyncSelectorV1Impl: DataSyncSelectorV1Impl): DataSyncSelectorV1
@Binds fun bindDataSyncSelectorV3Interface(dataSyncSelectorV3Impl: DataSyncSelectorV3Impl): DataSyncSelectorV3
@Binds fun bindDataSyncSelectorXdripInterface(dataSyncSelectorXdripImpl: DataSyncSelectorXdripImpl): DataSyncSelectorXdrip @Binds fun bindDataSyncSelectorXdripInterface(dataSyncSelectorXdripImpl: DataSyncSelectorXdripImpl): DataSyncSelectorXdrip
@Binds fun bindStoreDataForDb(storeDataForDbImpl: StoreDataForDbImpl): StoreDataForDb @Binds fun bindStoreDataForDb(storeDataForDbImpl: StoreDataForDbImpl): StoreDataForDb
@Binds fun bindXDripBroadcastInterface(xDripBroadcastImpl: XdripPlugin): XDripBroadcast @Binds fun bindXDripBroadcastInterface(xDripBroadcastImpl: XdripPlugin): XDripBroadcast

View file

@ -123,7 +123,7 @@ class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
nsClientPlugin?.listLog?.let { nsClientPlugin?.listLog?.let {
synchronized(it) { synchronized(it) {
it.clear() it.clear()
_binding?.recyclerview?.swapAdapter(RecyclerViewAdapter(it), true) updateLog()
} }
} }
true true
@ -211,6 +211,7 @@ class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
.subscribe({ updateStatus() }, fabricPrivacy::logException) .subscribe({ updateStatus() }, fabricPrivacy::logException)
updateStatus() updateStatus()
updateQueue() updateQueue()
updateLog()
} }
@Synchronized @Synchronized
@ -231,6 +232,9 @@ class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
binding.status.text = nsClientPlugin?.status binding.status.text = nsClientPlugin?.status
} }
private fun updateLog() {
_binding?.recyclerview?.swapAdapter(RecyclerViewAdapter(nsClientPlugin?.listLog ?: arrayListOf()), true)
}
private inner class RecyclerViewAdapter(private var logList: List<EventNSClientNewLog>) : RecyclerView.Adapter<RecyclerViewAdapter.NsClientLogViewHolder>() { private inner class RecyclerViewAdapter(private var logList: List<EventNSClientNewLog>) : RecyclerView.Adapter<RecyclerViewAdapter.NsClientLogViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): NsClientLogViewHolder = override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): NsClientLogViewHolder =

View file

@ -0,0 +1,807 @@
package info.nightscout.plugins.sync.nsclient
import info.nightscout.core.utils.waitMillis
import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.utils.JsonHelper
import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiQueue
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiStatus
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class DataSyncSelectorV1 @Inject constructor(
private val sp: SP,
private val aapsLogger: AAPSLogger,
private val dateUtil: DateUtil,
private val profileFunction: ProfileFunction,
private val activePlugin: ActivePlugin,
private val appRepository: AppRepository,
private val rxBus: RxBus
) : DataSyncSelector {
private var scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
class QueueCounter(
var bolusesRemaining: Long = -1L,
var carbsRemaining: Long = -1L,
var bcrRemaining: Long = -1L,
var ttsRemaining: Long = -1L,
var foodsRemaining: Long = -1L,
var gvsRemaining: Long = -1L,
var tesRemaining: Long = -1L,
var dssRemaining: Long = -1L,
var tbrsRemaining: Long = -1L,
var ebsRemaining: Long = -1L,
var pssRemaining: Long = -1L,
var epssRemaining: Long = -1L,
var oesRemaining: Long = -1L
) {
fun size(): Long =
bolusesRemaining +
carbsRemaining +
bcrRemaining +
ttsRemaining +
foodsRemaining +
gvsRemaining +
tesRemaining +
dssRemaining +
tbrsRemaining +
ebsRemaining +
pssRemaining +
epssRemaining +
oesRemaining
}
private val queueCounter = QueueCounter()
private val isPaused get() = sp.getBoolean(R.string.key_ns_paused, false)
override fun queueSize(): Long = queueCounter.size()
private var running = false
private val sync = Any()
override suspend fun doUpload() {
synchronized(sync) {
if (running) {
rxBus.send(EventNSClientNewLog("● RUN", "Already running"))
return
}
running = true
}
rxBus.send(EventNSClientUpdateGuiStatus())
if (sp.getBoolean(R.string.key_ns_upload, true) && !isPaused) {
queueCounter.bolusesRemaining = (appRepository.getLastBolusId() ?: 0L) - sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)
queueCounter.carbsRemaining = (appRepository.getLastCarbsId() ?: 0L) - sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)
queueCounter.bcrRemaining = (appRepository.getLastBolusCalculatorResultId() ?: 0L) - sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)
queueCounter.ttsRemaining = (appRepository.getLastTempTargetId() ?: 0L) - sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)
queueCounter.foodsRemaining = (appRepository.getLastFoodId() ?: 0L) - sp.getLong(R.string.key_ns_food_last_synced_id, 0)
queueCounter.gvsRemaining = (appRepository.getLastGlucoseValueId() ?: 0L) - sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)
queueCounter.tesRemaining = (appRepository.getLastTherapyEventId() ?: 0L) - sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)
queueCounter.dssRemaining = (appRepository.getLastDeviceStatusId() ?: 0L) - sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)
queueCounter.tbrsRemaining = (appRepository.getLastTemporaryBasalId() ?: 0L) - sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)
queueCounter.ebsRemaining = (appRepository.getLastExtendedBolusId() ?: 0L) - sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)
queueCounter.pssRemaining = (appRepository.getLastProfileSwitchId() ?: 0L) - sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)
queueCounter.epssRemaining = (appRepository.getLastEffectiveProfileSwitchId() ?: 0L) - sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
queueCounter.oesRemaining = (appRepository.getLastOfflineEventId() ?: 0L) - sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)
rxBus.send(EventNSClientUpdateGuiQueue())
val boluses = scope.async { processChangedBoluses() }
val carbs = scope.async { processChangedCarbs() }
val bcs = scope.async { processChangedBolusCalculatorResults() }
val tbs = scope.async { processChangedTemporaryBasals() }
val ebs = scope.async { processChangedExtendedBoluses() }
val pss = scope.async { processChangedProfileSwitches() }
val epss = scope.async { processChangedEffectiveProfileSwitches() }
val gvs = scope.async { processChangedGlucoseValues() }
val tts = scope.async { processChangedTempTargets() }
val foods = scope.async { processChangedFoods() }
val tes = scope.async { processChangedTherapyEvents() }
val dss = scope.async { processChangedDeviceStatuses() }
val oes = scope.async { processChangedOfflineEvents() }
val ps = scope.async { processChangedProfileStore() }
boluses.await()
carbs.await()
bcs.await()
tbs.await()
ebs.await()
pss.await()
epss.await()
gvs.await()
tts.await()
foods.await()
tes.await()
dss.await()
oes.await()
ps.await()
}
rxBus.send(EventNSClientUpdateGuiStatus())
running = false
}
override fun resetToNextFullSync() {
sp.remove(R.string.key_ns_glucose_value_last_synced_id)
sp.remove(R.string.key_ns_temporary_basal_last_synced_id)
sp.remove(R.string.key_ns_temporary_target_last_synced_id)
sp.remove(R.string.key_ns_extended_bolus_last_synced_id)
sp.remove(R.string.key_ns_food_last_synced_id)
sp.remove(R.string.key_ns_bolus_last_synced_id)
sp.remove(R.string.key_ns_carbs_last_synced_id)
sp.remove(R.string.key_ns_bolus_calculator_result_last_synced_id)
sp.remove(R.string.key_ns_therapy_event_last_synced_id)
sp.remove(R.string.key_ns_profile_switch_last_synced_id)
sp.remove(R.string.key_ns_effective_profile_switch_last_synced_id)
sp.remove(R.string.key_ns_offline_event_last_synced_id)
sp.remove(R.string.key_ns_profile_store_last_synced_timestamp)
val lastDeviceStatusDbId = appRepository.getLastDeviceStatusId()
if (lastDeviceStatusDbId != null) sp.putLong(R.string.key_ns_device_status_last_synced_id, lastDeviceStatusDbId)
else sp.remove(R.string.key_ns_device_status_last_synced_id)
}
private fun confirmLastBolusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting Bolus data sync from $lastSynced")
sp.putLong(R.string.key_ns_bolus_last_synced_id, lastSynced)
}
}
private suspend fun processChangedBoluses() {
var cont = true
while (cont) {
if (isPaused) return
val lastDbId = appRepository.getLastBolusId() ?: 0L
var startId = sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_bolus_last_synced_id, 0)
startId = 0
}
queueCounter.bolusesRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementBolus(startId).blockingGet()?.let { bolus ->
aapsLogger.info(LTag.NSCLIENT, "Loading Bolus data Start: $startId ${bolus.first} forID: ${bolus.second.id} ")
val dataPair = DataSyncSelector.PairBolus(bolus.first, bolus.second.id)
when {
// new record with existing NS id => must be coming from NS => ignore
bolus.first.id == bolus.second.id && bolus.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring Bolus. Loaded from NS: ${bolus.second.id} ")
// only NsId changed, no need to upload
bolus.first.onlyNsIdAdded(bolus.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring Bolus. Only NS id changed: ${bolus.second.id} ")
// without nsId = create new
bolus.first.interfaceIDs.nightscoutId == null -> {
activePlugin.activeNsClient?.nsAdd("treatments", dataPair, " $startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
// with nsId = update if it's modified record
bolus.first.interfaceIDs.nightscoutId != null && bolus.first.id != bolus.second.id -> {
activePlugin.activeNsClient?.nsUpdate("treatments", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
}
if (cont) confirmLastBolusIdIfGreater(bolus.second.id)
} ?: run {
cont = false
}
}
}
private fun confirmLastCarbsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting Carbs data sync from $lastSynced")
sp.putLong(R.string.key_ns_carbs_last_synced_id, lastSynced)
}
}
private suspend fun processChangedCarbs() {
var cont = true
while (cont) {
if (isPaused) return
val lastDbId = appRepository.getLastCarbsId() ?: 0L
var startId = sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_carbs_last_synced_id, 0)
startId = 0
}
queueCounter.carbsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementCarbs(startId).blockingGet()?.let { carb ->
aapsLogger.info(LTag.NSCLIENT, "Loading Carbs data Start: $startId ${carb.first} forID: ${carb.second.id} ")
val dataPair = DataSyncSelector.PairCarbs(carb.first, carb.second.id)
when {
// new record with existing NS id => must be coming from NS => ignore
carb.first.id == carb.second.id && carb.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring Carbs. Loaded from NS: ${carb.second.id} ")
// only NsId changed, no need to upload
carb.first.onlyNsIdAdded(carb.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring Carbs. Only NS id changed ID: ${carb.second.id} ")
// without nsId = create new
carb.first.interfaceIDs.nightscoutId == null -> {
activePlugin.activeNsClient?.nsAdd("treatments", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
// with nsId = update if it's modified record
carb.first.interfaceIDs.nightscoutId != null && carb.first.id != carb.second.id -> {
activePlugin.activeNsClient?.nsUpdate("treatments", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
}
if (cont) confirmLastCarbsIdIfGreater(carb.second.id)
} ?: run {
cont = false
}
}
}
private fun confirmLastBolusCalculatorResultsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting BolusCalculatorResult data sync from $lastSynced")
sp.putLong(R.string.key_ns_bolus_calculator_result_last_synced_id, lastSynced)
}
}
private suspend fun processChangedBolusCalculatorResults() {
var cont = true
while (cont) {
if (isPaused) return
val lastDbId = appRepository.getLastBolusCalculatorResultId() ?: 0L
var startId = sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)
startId = 0
}
queueCounter.bcrRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementBolusCalculatorResult(startId).blockingGet()?.let { bolusCalculatorResult ->
aapsLogger.info(LTag.NSCLIENT, "Loading BolusCalculatorResult data Start: $startId ${bolusCalculatorResult.first} forID: ${bolusCalculatorResult.second.id} ")
val dataPair = DataSyncSelector.PairBolusCalculatorResult(bolusCalculatorResult.first, bolusCalculatorResult.second.id)
when {
// new record with existing NS id => must be coming from NS => ignore
bolusCalculatorResult.first.id == bolusCalculatorResult.second.id && bolusCalculatorResult.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring BolusCalculatorResult. Loaded from NS: ${bolusCalculatorResult.second.id} ")
// only NsId changed, no need to upload
bolusCalculatorResult.first.onlyNsIdAdded(bolusCalculatorResult.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring BolusCalculatorResult. Only NS id changed ID: ${bolusCalculatorResult.second.id} ")
// without nsId = create new
bolusCalculatorResult.first.interfaceIDs.nightscoutId == null -> {
activePlugin.activeNsClient?.nsAdd("treatments", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
// with nsId = update if it's modified record
bolusCalculatorResult.first.interfaceIDs.nightscoutId != null && bolusCalculatorResult.first.id != bolusCalculatorResult.second.id -> {
activePlugin.activeNsClient?.nsUpdate("treatments", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
}
if (cont) confirmLastBolusCalculatorResultsIdIfGreater(bolusCalculatorResult.second.id)
} ?: run {
cont = false
}
}
}
private fun confirmLastTempTargetsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting TemporaryTarget data sync from $lastSynced")
sp.putLong(R.string.key_ns_temporary_target_last_synced_id, lastSynced)
}
}
private suspend fun processChangedTempTargets() {
var cont = true
while (cont) {
if (isPaused) return
val lastDbId = appRepository.getLastTempTargetId() ?: 0L
var startId = sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_temporary_target_last_synced_id, 0)
startId = 0
}
queueCounter.ttsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementTemporaryTarget(startId).blockingGet()?.let { tt ->
aapsLogger.info(LTag.NSCLIENT, "Loading TemporaryTarget data Start: $startId ${tt.first} forID: ${tt.second.id} ")
val dataPair = DataSyncSelector.PairTemporaryTarget(tt.first, tt.second.id)
when {
// new record with existing NS id => must be coming from NS => ignore
tt.first.id == tt.second.id && tt.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryTarget. Loaded from NS: ${tt.second.id} ")
// only NsId changed, no need to upload
tt.first.onlyNsIdAdded(tt.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryTarget. Only NS id changed ID: ${tt.second.id} ")
// without nsId = create new
tt.first.interfaceIDs.nightscoutId == null -> {
activePlugin.activeNsClient?.nsAdd("treatments", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
// existing with nsId = update
tt.first.interfaceIDs.nightscoutId != null -> {
activePlugin.activeNsClient?.nsUpdate("treatments", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
}
if (cont) confirmLastTempTargetsIdIfGreater(tt.second.id)
} ?: run {
cont = false
}
}
}
private fun confirmLastFoodIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_food_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting Food data sync from $lastSynced")
sp.putLong(R.string.key_ns_food_last_synced_id, lastSynced)
}
}
private suspend fun processChangedFoods() {
var cont = true
while (cont) {
if (isPaused) return
val lastDbId = appRepository.getLastFoodId() ?: 0L
var startId = sp.getLong(R.string.key_ns_food_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_food_last_synced_id, 0)
startId = 0
}
queueCounter.foodsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementFood(startId).blockingGet()?.let { food ->
aapsLogger.info(LTag.NSCLIENT, "Loading Food data Start: $startId ${food.first} forID: ${food.second.id} ")
val dataPair = DataSyncSelector.PairFood(food.first, food.second.id)
when {
// new record with existing NS id => must be coming from NS => ignore
food.first.id == food.second.id && food.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring Food. Loaded from NS: ${food.second.id} ")
// only NsId changed, no need to upload
food.first.onlyNsIdAdded(food.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring Food. Only NS id changed ID: ${food.second.id} ")
// without nsId = create new
food.first.interfaceIDs.nightscoutId == null -> {
activePlugin.activeNsClient?.nsAdd("food", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
// with nsId = update
food.first.interfaceIDs.nightscoutId != null -> {
activePlugin.activeNsClient?.nsUpdate("food", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
}
if (cont) confirmLastFoodIdIfGreater(food.second.id)
} ?: run {
cont = false
}
}
}
private fun confirmLastGlucoseValueIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting GlucoseValue data sync from $lastSynced")
sp.putLong(R.string.key_ns_glucose_value_last_synced_id, lastSynced)
}
}
private suspend fun processChangedGlucoseValues() {
var cont = true
while (cont) {
if (isPaused) return
val lastDbId = appRepository.getLastGlucoseValueId() ?: 0L
var startId = sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_glucose_value_last_synced_id, 0)
startId = 0
}
queueCounter.gvsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementGlucoseValue(startId).blockingGet()?.let { gv ->
aapsLogger.info(LTag.NSCLIENT, "Loading GlucoseValue data Start: $startId ${gv.first} forID: ${gv.second.id} ")
val dataPair = DataSyncSelector.PairGlucoseValue(gv.first, gv.second.id)
if (activePlugin.activeBgSource.shouldUploadToNs(gv.first)) {
when {
// new record with existing NS id => must be coming from NS => ignore
gv.first.id == gv.second.id && gv.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring GlucoseValue. Loaded from NS: ${gv.second.id} ")
// only NsId changed, no need to upload
gv.first.onlyNsIdAdded(gv.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring GlucoseValue. Only NS id changed ID: ${gv.second.id} ")
// without nsId = create new
gv.first.interfaceIDs.nightscoutId == null -> {
activePlugin.activeNsClient?.nsAdd("entries", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
// with nsId = update
else -> {// gv.first.interfaceIDs.nightscoutId != null
activePlugin.activeNsClient?.nsUpdate("entries", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
}
}
if (cont) confirmLastGlucoseValueIdIfGreater(gv.second.id)
} ?: run {
cont = false
}
}
}
private fun confirmLastTherapyEventIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting TherapyEvents data sync from $lastSynced")
sp.putLong(R.string.key_ns_therapy_event_last_synced_id, lastSynced)
}
}
private suspend fun processChangedTherapyEvents() {
var cont = true
while (cont) {
if (isPaused) return
val lastDbId = appRepository.getLastTherapyEventId() ?: 0L
var startId = sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_therapy_event_last_synced_id, 0)
startId = 0
}
queueCounter.tesRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementTherapyEvent(startId).blockingGet()?.let { te ->
aapsLogger.info(LTag.NSCLIENT, "Loading TherapyEvents data Start: $startId ${te.first} forID: ${te.second.id} ")
val dataPair = DataSyncSelector.PairTherapyEvent(te.first, te.second.id)
when {
// new record with existing NS id => must be coming from NS => ignore
te.first.id == te.second.id && te.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring TherapyEvent. Loaded from NS: ${te.second.id} ")
// only NsId changed, no need to upload
te.first.onlyNsIdAdded(te.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring TherapyEvent. Only NS id changed ID: ${te.second.id} ")
// without nsId = create new
te.first.interfaceIDs.nightscoutId == null -> {
activePlugin.activeNsClient?.nsAdd("treatments", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
// nsId = update
te.first.interfaceIDs.nightscoutId != null -> {
activePlugin.activeNsClient?.nsUpdate("treatments", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
}
if (cont) confirmLastTherapyEventIdIfGreater(te.second.id)
} ?: run {
cont = false
}
}
}
private fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting DeviceStatus data sync from $lastSynced")
sp.putLong(R.string.key_ns_device_status_last_synced_id, lastSynced)
}
}
private suspend fun processChangedDeviceStatuses() {
var cont = true
while (cont) {
if (isPaused) return
val lastDbId = appRepository.getLastDeviceStatusId() ?: 0L
var startId = sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_device_status_last_synced_id, 0)
startId = 0
}
queueCounter.dssRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementDeviceStatus(startId).blockingGet()?.let { deviceStatus ->
aapsLogger.info(LTag.NSCLIENT, "Loading DeviceStatus data Start: $startId $deviceStatus")
val dataPair = DataSyncSelector.PairDeviceStatus(deviceStatus, lastDbId)
activePlugin.activeNsClient?.nsAdd("devicestatus", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
if (cont) confirmLastDeviceStatusIdIfGreater(deviceStatus.id)
} ?: run {
cont = false
}
}
}
private fun confirmLastTemporaryBasalIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting TemporaryBasal data sync from $lastSynced")
sp.putLong(R.string.key_ns_temporary_basal_last_synced_id, lastSynced)
}
}
private suspend fun processChangedTemporaryBasals() {
var cont = true
while (cont) {
if (isPaused) return
val lastDbId = appRepository.getLastTemporaryBasalId() ?: 0L
var startId = sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_temporary_basal_last_synced_id, 0)
startId = 0
}
queueCounter.tbrsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementTemporaryBasal(startId).blockingGet()?.let { tb ->
aapsLogger.info(LTag.NSCLIENT, "Loading TemporaryBasal data Start: $startId ${tb.first} forID: ${tb.second.id} ")
val dataPair = DataSyncSelector.PairTemporaryBasal(tb.first, tb.second.id)
val profile = profileFunction.getProfile(tb.first.timestamp)
if (profile != null) {
when {
// new record with existing NS id => must be coming from NS => ignore
tb.first.id == tb.second.id && tb.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryBasal. Loaded from NS: ${tb.second.id} ")
// only NsId changed, no need to upload
tb.first.onlyNsIdAdded(tb.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryBasal. Only NS id changed ID: ${tb.second.id} ")
// without nsId = create new
tb.first.interfaceIDs.nightscoutId == null -> {
activePlugin.activeNsClient?.nsAdd("treatments", dataPair, "$startId/$lastDbId", profile)
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
// with nsId = update
tb.first.interfaceIDs.nightscoutId != null -> {
activePlugin.activeNsClient?.nsUpdate("treatments", dataPair, "$startId/$lastDbId", profile)
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
}
} else aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryBasal. No profile: ${tb.second.id} ")
if (cont) confirmLastTemporaryBasalIdIfGreater(tb.second.id)
} ?: run {
cont = false
}
}
}
private fun confirmLastExtendedBolusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting ExtendedBolus data sync from $lastSynced")
sp.putLong(R.string.key_ns_extended_bolus_last_synced_id, lastSynced)
}
}
private suspend fun processChangedExtendedBoluses() {
var cont = true
while (cont) {
if (isPaused) return
val lastDbId = appRepository.getLastExtendedBolusId() ?: 0L
var startId = sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_extended_bolus_last_synced_id, 0)
startId = 0
}
queueCounter.ebsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementExtendedBolus(startId).blockingGet()?.let { eb ->
aapsLogger.info(LTag.NSCLIENT, "Loading ExtendedBolus data Start: $startId ${eb.first} forID: ${eb.second.id} ")
val dataPair = DataSyncSelector.PairExtendedBolus(eb.first, eb.second.id)
val profile = profileFunction.getProfile(eb.first.timestamp)
if (profile != null) {
when {
// new record with existing NS id => must be coming from NS => ignore
eb.first.id == eb.second.id && eb.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring ExtendedBolus. Loaded from NS: ${eb.second.id} ")
// only NsId changed, no need to upload
eb.first.onlyNsIdAdded(eb.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring ExtendedBolus. Only NS id changed ID: ${eb.second.id} ")
// without nsId = create new
eb.first.interfaceIDs.nightscoutId == null -> {
activePlugin.activeNsClient?.nsAdd("treatments", dataPair, "$startId/$lastDbId", profile)
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
// with nsId = update
eb.first.interfaceIDs.nightscoutId != null -> {
activePlugin.activeNsClient?.nsUpdate("treatments", dataPair, "$startId/$lastDbId", profile)
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
}
} else aapsLogger.info(LTag.NSCLIENT, "Ignoring ExtendedBolus. No profile: ${eb.second.id} ")
if (cont) confirmLastExtendedBolusIdIfGreater(eb.second.id)
} ?: run {
cont = false
}
}
}
private fun confirmLastProfileSwitchIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting ProfileSwitch data sync from $lastSynced")
sp.putLong(R.string.key_ns_profile_switch_last_synced_id, lastSynced)
}
}
private suspend fun processChangedProfileSwitches() {
var cont = true
while (cont) {
if (isPaused) return
val lastDbId = appRepository.getLastProfileSwitchId() ?: 0L
var startId = sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_profile_switch_last_synced_id, 0)
startId = 0
}
queueCounter.pssRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementProfileSwitch(startId).blockingGet()?.let { ps ->
aapsLogger.info(LTag.NSCLIENT, "Loading ProfileSwitch data Start: $startId ${ps.first} forID: ${ps.second.id} ")
val dataPair = DataSyncSelector.PairProfileSwitch(ps.first, ps.second.id)
when {
// new record with existing NS id => must be coming from NS => ignore
ps.first.id == ps.second.id && ps.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring ProfileSwitch. Loaded from NS: ${ps.second.id} ")
// only NsId changed, no need to upload
ps.first.onlyNsIdAdded(ps.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring ProfileSwitch. Only NS id changed ID: ${ps.second.id} ")
// without nsId = create new
ps.first.interfaceIDs.nightscoutId == null -> {
activePlugin.activeNsClient?.nsAdd("treatments", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
// with nsId = update
ps.first.interfaceIDs.nightscoutId != null -> {
activePlugin.activeNsClient?.nsUpdate("treatments", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
}
if (cont) confirmLastProfileSwitchIdIfGreater(ps.second.id)
} ?: run {
cont = false
}
}
}
private fun confirmLastEffectiveProfileSwitchIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting EffectiveProfileSwitch data sync from $lastSynced")
sp.putLong(R.string.key_ns_effective_profile_switch_last_synced_id, lastSynced)
}
}
private suspend fun processChangedEffectiveProfileSwitches() {
var cont = true
while (cont) {
if (isPaused) return
val lastDbId = appRepository.getLastEffectiveProfileSwitchId() ?: 0L
var startId = sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
startId = 0
}
queueCounter.epssRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementEffectiveProfileSwitch(startId).blockingGet()?.let { ps ->
aapsLogger.info(LTag.NSCLIENT, "Loading EffectiveProfileSwitch data Start: $startId ${ps.first} forID: ${ps.second.id} ")
val dataPair = DataSyncSelector.PairEffectiveProfileSwitch(ps.first, ps.second.id)
when {
// new record with existing NS id => must be coming from NS => ignore
ps.first.id == ps.second.id && ps.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring EffectiveProfileSwitch. Loaded from NS: ${ps.second.id} ")
// only NsId changed, no need to upload
ps.first.onlyNsIdAdded(ps.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring EffectiveProfileSwitch. Only NS id changed ID: ${ps.second.id} ")
// without nsId = create new
ps.first.interfaceIDs.nightscoutId == null -> {
activePlugin.activeNsClient?.nsAdd("treatments", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
// with nsId = update
ps.first.interfaceIDs.nightscoutId != null -> {
activePlugin.activeNsClient?.nsUpdate("treatments", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
}
if (cont) confirmLastEffectiveProfileSwitchIdIfGreater(ps.second.id)
} ?: run {
cont = false
}
}
}
private fun confirmLastOfflineEventIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting OfflineEvent data sync from $lastSynced")
sp.putLong(R.string.key_ns_offline_event_last_synced_id, lastSynced)
}
}
private suspend fun processChangedOfflineEvents() {
var cont = true
while (cont) {
if (isPaused) return
val lastDbId = appRepository.getLastOfflineEventId() ?: 0L
var startId = sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_offline_event_last_synced_id, 0)
startId = 0
}
queueCounter.oesRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementOfflineEvent(startId).blockingGet()?.let { oe ->
aapsLogger.info(LTag.NSCLIENT, "Loading OfflineEvent data Start: $startId ${oe.first} forID: ${oe.second.id} ")
val dataPair = DataSyncSelector.PairOfflineEvent(oe.first, oe.second.id)
when {
// new record with existing NS id => must be coming from NS => ignore
oe.first.id == oe.second.id && oe.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring OfflineEvent. Loaded from NS: ${oe.second.id} ")
// only NsId changed, no need to upload
oe.first.onlyNsIdAdded(oe.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring OfflineEvent. Only NS id changed ID: ${oe.second.id} ")
// without nsId = create new
oe.first.interfaceIDs.nightscoutId == null -> {
activePlugin.activeNsClient?.nsAdd("treatments", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
// existing with nsId = update
oe.first.interfaceIDs.nightscoutId != null -> {
activePlugin.activeNsClient?.nsUpdate("treatments", dataPair, "$startId/$lastDbId")
synchronized(dataPair) { dataPair.waitMillis(60000) }
cont = dataPair.confirmed
}
}
if (cont) confirmLastOfflineEventIdIfGreater(oe.second.id)
} ?: run {
cont = false
}
}
}
private fun confirmLastProfileStore(lastSynced: Long) {
sp.putLong(R.string.key_ns_profile_store_last_synced_timestamp, lastSynced)
}
private suspend fun processChangedProfileStore() {
if (isPaused) return
val lastSync = sp.getLong(R.string.key_ns_profile_store_last_synced_timestamp, 0)
val lastChange = sp.getLong(info.nightscout.core.utils.R.string.key_local_profile_last_change, 0)
if (lastChange == 0L) return
if (lastChange > lastSync) {
if (activePlugin.activeProfileSource.profile?.allProfilesValid != true) return
val profileStore = activePlugin.activeProfileSource.profile
val profileJson = profileStore?.data ?: return
// add for v3
if (JsonHelper.safeGetLongAllowNull(profileJson, "date") == null)
profileJson.put("date", profileStore.getStartDate())
val dataPair = DataSyncSelector.PairProfileStore(profileJson, dateUtil.now())
activePlugin.activeNsClient?.nsAdd("profile", dataPair, "")
synchronized(dataPair) { dataPair.waitMillis(60000) }
val now = dateUtil.now()
val cont = dataPair.confirmed
if (cont) confirmLastProfileStore(now)
}
}
}

View file

@ -1,737 +0,0 @@
package info.nightscout.plugins.sync.nsclient
import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.DataSyncSelectorV1
import info.nightscout.interfaces.utils.JsonHelper
import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiQueue
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiStatus
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class DataSyncSelectorV1Impl @Inject constructor(
private val sp: SP,
private val aapsLogger: AAPSLogger,
private val dateUtil: DateUtil,
private val profileFunction: ProfileFunction,
private val activePlugin: ActivePlugin,
private val appRepository: AppRepository,
private val rxBus: RxBus
) : DataSyncSelectorV1 {
class QueueCounter(
var bolusesRemaining: Long = -1L,
var carbsRemaining: Long = -1L,
var bcrRemaining: Long = -1L,
var ttsRemaining: Long = -1L,
var foodsRemaining: Long = -1L,
var gvsRemaining: Long = -1L,
var tesRemaining: Long = -1L,
var dssRemaining: Long = -1L,
var tbrsRemaining: Long = -1L,
var ebsRemaining: Long = -1L,
var pssRemaining: Long = -1L,
var epssRemaining: Long = -1L,
var oesRemaining: Long = -1L
) {
fun size(): Long =
bolusesRemaining +
carbsRemaining +
bcrRemaining +
ttsRemaining +
foodsRemaining +
gvsRemaining +
tesRemaining +
dssRemaining +
tbrsRemaining +
ebsRemaining +
pssRemaining +
epssRemaining +
oesRemaining
}
private val queueCounter = QueueCounter()
private val isPaused get() = sp.getBoolean(R.string.key_ns_paused, false)
override fun queueSize(): Long = queueCounter.size()
override suspend fun doUpload() {
rxBus.send(EventNSClientUpdateGuiStatus())
if (sp.getBoolean(R.string.key_ns_upload, true) && !isPaused) {
queueCounter.bolusesRemaining = (appRepository.getLastBolusId() ?: 0L) - sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)
queueCounter.carbsRemaining = (appRepository.getLastCarbsId() ?: 0L) - sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)
queueCounter.bcrRemaining = (appRepository.getLastBolusCalculatorResultId() ?: 0L) - sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)
queueCounter.ttsRemaining = (appRepository.getLastTempTargetId() ?: 0L) - sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)
queueCounter.foodsRemaining = (appRepository.getLastFoodId() ?: 0L) - sp.getLong(R.string.key_ns_food_last_synced_id, 0)
queueCounter.gvsRemaining = (appRepository.getLastGlucoseValueId() ?: 0L) - sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)
queueCounter.tesRemaining = (appRepository.getLastTherapyEventId() ?: 0L) - sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)
queueCounter.dssRemaining = (appRepository.getLastDeviceStatusId() ?: 0L) - sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)
queueCounter.tbrsRemaining = (appRepository.getLastTemporaryBasalId() ?: 0L) - sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)
queueCounter.ebsRemaining = (appRepository.getLastExtendedBolusId() ?: 0L) - sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)
queueCounter.pssRemaining = (appRepository.getLastProfileSwitchId() ?: 0L) - sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)
queueCounter.epssRemaining = (appRepository.getLastEffectiveProfileSwitchId() ?: 0L) - sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
queueCounter.oesRemaining = (appRepository.getLastOfflineEventId() ?: 0L) - sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)
rxBus.send(EventNSClientUpdateGuiQueue())
processChangedBoluses()
processChangedCarbs()
processChangedBolusCalculatorResults()
processChangedTemporaryBasals()
processChangedExtendedBoluses()
processChangedProfileSwitches()
processChangedEffectiveProfileSwitches()
processChangedGlucoseValues()
processChangedTempTargets()
processChangedFoods()
processChangedTherapyEvents()
processChangedDeviceStatuses()
processChangedOfflineEvents()
processChangedProfileStore()
}
rxBus.send(EventNSClientUpdateGuiStatus())
}
override fun resetToNextFullSync() {
sp.remove(R.string.key_ns_glucose_value_last_synced_id)
sp.remove(R.string.key_ns_temporary_basal_last_synced_id)
sp.remove(R.string.key_ns_temporary_target_last_synced_id)
sp.remove(R.string.key_ns_extended_bolus_last_synced_id)
sp.remove(R.string.key_ns_food_last_synced_id)
sp.remove(R.string.key_ns_bolus_last_synced_id)
sp.remove(R.string.key_ns_carbs_last_synced_id)
sp.remove(R.string.key_ns_bolus_calculator_result_last_synced_id)
sp.remove(R.string.key_ns_therapy_event_last_synced_id)
sp.remove(R.string.key_ns_profile_switch_last_synced_id)
sp.remove(R.string.key_ns_effective_profile_switch_last_synced_id)
sp.remove(R.string.key_ns_offline_event_last_synced_id)
sp.remove(R.string.key_ns_profile_store_last_synced_timestamp)
val lastDeviceStatusDbId = appRepository.getLastDeviceStatusId()
if (lastDeviceStatusDbId != null) sp.putLong(R.string.key_ns_device_status_last_synced_id, lastDeviceStatusDbId)
else sp.remove(R.string.key_ns_device_status_last_synced_id)
}
override fun confirmLastBolusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting Bolus data sync from $lastSynced")
sp.putLong(R.string.key_ns_bolus_last_synced_id, lastSynced)
}
}
override tailrec suspend fun processChangedBoluses() {
if (isPaused) return
val lastDbId = appRepository.getLastBolusId() ?: 0L
var startId = sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_bolus_last_synced_id, 0)
startId = 0
}
queueCounter.bolusesRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementBolus(startId).blockingGet()?.let { bolus ->
aapsLogger.info(LTag.NSCLIENT, "Loading Bolus data Start: $startId ${bolus.first} forID: ${bolus.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
bolus.first.id == bolus.second.id && bolus.first.interfaceIDs.nightscoutId != null -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring Bolus. Loaded from NS: ${bolus.second.id} ")
confirmLastBolusIdIfGreater(bolus.second.id)
processChangedBoluses()
return
}
// only NsId changed, no need to upload
bolus.first.onlyNsIdAdded(bolus.second) -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring Bolus. Only NS id changed: ${bolus.second.id} ")
confirmLastBolusIdIfGreater(bolus.second.id)
processChangedBoluses()
return
}
// without nsId = create new
bolus.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairBolus(bolus.first, bolus.second.id), " $startId/$lastDbId")
// with nsId = update if it's modified record
bolus.first.interfaceIDs.nightscoutId != null && bolus.first.id != bolus.second.id ->
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairBolus(bolus.first, bolus.second.id), "$startId/$lastDbId")
}
return
}
}
override fun confirmLastCarbsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting Carbs data sync from $lastSynced")
sp.putLong(R.string.key_ns_carbs_last_synced_id, lastSynced)
}
}
override tailrec suspend fun processChangedCarbs() {
if (isPaused) return
val lastDbId = appRepository.getLastCarbsId() ?: 0L
var startId = sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_carbs_last_synced_id, 0)
startId = 0
}
queueCounter.carbsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementCarbs(startId).blockingGet()?.let { carb ->
aapsLogger.info(LTag.NSCLIENT, "Loading Carbs data Start: $startId ${carb.first} forID: ${carb.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
carb.first.id == carb.second.id && carb.first.interfaceIDs.nightscoutId != null -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring Carbs. Loaded from NS: ${carb.second.id} ")
confirmLastCarbsIdIfGreater(carb.second.id)
processChangedCarbs()
return
}
// only NsId changed, no need to upload
carb.first.onlyNsIdAdded(carb.second) -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring Carbs. Only NS id changed ID: ${carb.second.id} ")
confirmLastCarbsIdIfGreater(carb.second.id)
processChangedCarbs()
return
}
// without nsId = create new
carb.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairCarbs(carb.first, carb.second.id), "$startId/$lastDbId")
// with nsId = update if it's modified record
carb.first.interfaceIDs.nightscoutId != null && carb.first.id != carb.second.id ->
activePlugin.activeNsClient?.nsUpdate(
"treatments",
DataSyncSelector.PairCarbs(carb.first, carb.second.id),
"$startId/$lastDbId"
)
}
return
}
}
override fun confirmLastBolusCalculatorResultsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting BolusCalculatorResult data sync from $lastSynced")
sp.putLong(R.string.key_ns_bolus_calculator_result_last_synced_id, lastSynced)
}
}
override tailrec suspend fun processChangedBolusCalculatorResults() {
if (isPaused) return
val lastDbId = appRepository.getLastBolusCalculatorResultId() ?: 0L
var startId = sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)
startId = 0
}
queueCounter.bcrRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementBolusCalculatorResult(startId).blockingGet()?.let { bolusCalculatorResult ->
aapsLogger.info(LTag.NSCLIENT, "Loading BolusCalculatorResult data Start: $startId ${bolusCalculatorResult.first} forID: ${bolusCalculatorResult.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
bolusCalculatorResult.first.id == bolusCalculatorResult.second.id && bolusCalculatorResult.first.interfaceIDs.nightscoutId != null -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring BolusCalculatorResult. Loaded from NS: ${bolusCalculatorResult.second.id} ")
confirmLastBolusCalculatorResultsIdIfGreater(bolusCalculatorResult.second.id)
processChangedBolusCalculatorResults()
return
}
// only NsId changed, no need to upload
bolusCalculatorResult.first.onlyNsIdAdded(bolusCalculatorResult.second) -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring BolusCalculatorResult. Only NS id changed ID: ${bolusCalculatorResult.second.id} ")
confirmLastBolusCalculatorResultsIdIfGreater(bolusCalculatorResult.second.id)
processChangedBolusCalculatorResults()
return
}
// without nsId = create new
bolusCalculatorResult.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairBolusCalculatorResult(bolusCalculatorResult.first, bolusCalculatorResult.second.id), "$startId/$lastDbId")
// with nsId = update if it's modified record
bolusCalculatorResult.first.interfaceIDs.nightscoutId != null && bolusCalculatorResult.first.id != bolusCalculatorResult.second.id ->
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairBolusCalculatorResult(bolusCalculatorResult.first, bolusCalculatorResult.second.id), "$startId/$lastDbId")
}
return
}
}
override fun confirmLastTempTargetsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting TemporaryTarget data sync from $lastSynced")
sp.putLong(R.string.key_ns_temporary_target_last_synced_id, lastSynced)
}
}
override tailrec suspend fun processChangedTempTargets() {
if (isPaused) return
val lastDbId = appRepository.getLastTempTargetId() ?: 0L
var startId = sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_temporary_target_last_synced_id, 0)
startId = 0
}
queueCounter.ttsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementTemporaryTarget(startId).blockingGet()?.let { tt ->
aapsLogger.info(LTag.NSCLIENT, "Loading TemporaryTarget data Start: $startId ${tt.first} forID: ${tt.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
tt.first.id == tt.second.id && tt.first.interfaceIDs.nightscoutId != null -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryTarget. Loaded from NS: ${tt.second.id} ")
confirmLastTempTargetsIdIfGreater(tt.second.id)
processChangedTempTargets()
return
}
// only NsId changed, no need to upload
tt.first.onlyNsIdAdded(tt.second) -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryTarget. Only NS id changed ID: ${tt.second.id} ")
confirmLastTempTargetsIdIfGreater(tt.second.id)
processChangedTempTargets()
return
}
// without nsId = create new
tt.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairTemporaryTarget(tt.first, tt.second.id), "$startId/$lastDbId")
// existing with nsId = update
tt.first.interfaceIDs.nightscoutId != null ->
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairTemporaryTarget(tt.first, tt.second.id), "$startId/$lastDbId")
}
return
}
}
override fun confirmLastFoodIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_food_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting Food data sync from $lastSynced")
sp.putLong(R.string.key_ns_food_last_synced_id, lastSynced)
}
}
override tailrec suspend fun processChangedFoods() {
if (isPaused) return
val lastDbId = appRepository.getLastFoodId() ?: 0L
var startId = sp.getLong(R.string.key_ns_food_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_food_last_synced_id, 0)
startId = 0
}
queueCounter.foodsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementFood(startId).blockingGet()?.let { food ->
aapsLogger.info(LTag.NSCLIENT, "Loading Food data Start: $startId ${food.first} forID: ${food.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
food.first.id == food.second.id && food.first.interfaceIDs.nightscoutId != null -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring Food. Loaded from NS: ${food.second.id} ")
confirmLastFoodIdIfGreater(food.second.id)
processChangedFoods()
return
}
// only NsId changed, no need to upload
food.first.onlyNsIdAdded(food.second) -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring Food. Only NS id changed ID: ${food.second.id} ")
confirmLastFoodIdIfGreater(food.second.id)
processChangedFoods()
return
}
// without nsId = create new
food.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd("food", DataSyncSelector.PairFood(food.first, food.second.id), "$startId/$lastDbId")
// with nsId = update
food.first.interfaceIDs.nightscoutId != null ->
activePlugin.activeNsClient?.nsUpdate("food", DataSyncSelector.PairFood(food.first, food.second.id), "$startId/$lastDbId")
}
return
}
}
override fun confirmLastGlucoseValueIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting GlucoseValue data sync from $lastSynced")
sp.putLong(R.string.key_ns_glucose_value_last_synced_id, lastSynced)
}
}
override tailrec suspend fun processChangedGlucoseValues() {
if (isPaused) return
val lastDbId = appRepository.getLastGlucoseValueId() ?: 0L
var startId = sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_glucose_value_last_synced_id, 0)
startId = 0
}
queueCounter.gvsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementGlucoseValue(startId).blockingGet()?.let { gv ->
aapsLogger.info(LTag.NSCLIENT, "Loading GlucoseValue data Start: $startId ${gv.first} forID: ${gv.second.id} ")
if (activePlugin.activeBgSource.shouldUploadToNs(gv.first)) {
when {
// new record with existing NS id => must be coming from NS => ignore
gv.first.id == gv.second.id && gv.first.interfaceIDs.nightscoutId != null -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring GlucoseValue. Loaded from NS: ${gv.second.id} ")
confirmLastGlucoseValueIdIfGreater(gv.second.id)
processChangedGlucoseValues()
return
}
// only NsId changed, no need to upload
gv.first.onlyNsIdAdded(gv.second) -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring GlucoseValue. Only NS id changed ID: ${gv.second.id} ")
confirmLastGlucoseValueIdIfGreater(gv.second.id)
processChangedGlucoseValues()
return
}
// without nsId = create new
gv.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd("entries", DataSyncSelector.PairGlucoseValue(gv.first, gv.second.id), "$startId/$lastDbId")
// with nsId = update
else -> // gv.first.interfaceIDs.nightscoutId != null
activePlugin.activeNsClient?.nsUpdate("entries", DataSyncSelector.PairGlucoseValue(gv.first, gv.second.id), "$startId/$lastDbId")
}
} else {
confirmLastGlucoseValueIdIfGreater(gv.second.id)
processChangedGlucoseValues()
return
}
}
}
override fun confirmLastTherapyEventIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting TherapyEvents data sync from $lastSynced")
sp.putLong(R.string.key_ns_therapy_event_last_synced_id, lastSynced)
}
}
override tailrec suspend fun processChangedTherapyEvents() {
if (isPaused) return
val lastDbId = appRepository.getLastTherapyEventId() ?: 0L
var startId = sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_therapy_event_last_synced_id, 0)
startId = 0
}
queueCounter.tesRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementTherapyEvent(startId).blockingGet()?.let { te ->
aapsLogger.info(LTag.NSCLIENT, "Loading TherapyEvents data Start: $startId ${te.first} forID: ${te.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
te.first.id == te.second.id && te.first.interfaceIDs.nightscoutId != null -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring TherapyEvent. Loaded from NS: ${te.second.id} ")
confirmLastTherapyEventIdIfGreater(te.second.id)
processChangedTherapyEvents()
return
}
// only NsId changed, no need to upload
te.first.onlyNsIdAdded(te.second) -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring TherapyEvent. Only NS id changed ID: ${te.second.id} ")
confirmLastTherapyEventIdIfGreater(te.second.id)
processChangedTherapyEvents()
return
}
// without nsId = create new
te.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairTherapyEvent(te.first, te.second.id), "$startId/$lastDbId")
// nsId = update
te.first.interfaceIDs.nightscoutId != null ->
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairTherapyEvent(te.first, te.second.id), "$startId/$lastDbId")
}
return
}
}
override fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting DeviceStatus data sync from $lastSynced")
sp.putLong(R.string.key_ns_device_status_last_synced_id, lastSynced)
}
}
override suspend fun processChangedDeviceStatuses() {
if (isPaused) return
val lastDbId = appRepository.getLastDeviceStatusId() ?: 0L
var startId = sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_device_status_last_synced_id, 0)
startId = 0
}
queueCounter.dssRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementDeviceStatus(startId).blockingGet()?.let { deviceStatus ->
aapsLogger.info(LTag.NSCLIENT, "Loading DeviceStatus data Start: $startId $deviceStatus")
when {
// without nsId = create new
deviceStatus.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd("devicestatus", DataSyncSelector.PairDeviceStatus(deviceStatus, lastDbId), "$startId/$lastDbId")
// with nsId = ignore
deviceStatus.interfaceIDs.nightscoutId != null -> Any()
}
return
}
}
override fun confirmLastTemporaryBasalIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting TemporaryBasal data sync from $lastSynced")
sp.putLong(R.string.key_ns_temporary_basal_last_synced_id, lastSynced)
}
}
override tailrec suspend fun processChangedTemporaryBasals() {
if (isPaused) return
val lastDbId = appRepository.getLastTemporaryBasalId() ?: 0L
var startId = sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_temporary_basal_last_synced_id, 0)
startId = 0
}
queueCounter.tbrsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementTemporaryBasal(startId).blockingGet()?.let { tb ->
aapsLogger.info(LTag.NSCLIENT, "Loading TemporaryBasal data Start: $startId ${tb.first} forID: ${tb.second.id} ")
val profile = profileFunction.getProfile(tb.first.timestamp)
if (profile != null) {
when {
// new record with existing NS id => must be coming from NS => ignore
tb.first.id == tb.second.id && tb.first.interfaceIDs.nightscoutId != null -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryBasal. Loaded from NS: ${tb.second.id} ")
confirmLastTemporaryBasalIdIfGreater(tb.second.id)
processChangedTemporaryBasals()
return
}
// only NsId changed, no need to upload
tb.first.onlyNsIdAdded(tb.second) -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryBasal. Only NS id changed ID: ${tb.second.id} ")
confirmLastTemporaryBasalIdIfGreater(tb.second.id)
processChangedTemporaryBasals()
return
}
// without nsId = create new
tb.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairTemporaryBasal(tb.first, tb.second.id), "$startId/$lastDbId", profile)
// with nsId = update
tb.first.interfaceIDs.nightscoutId != null ->
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairTemporaryBasal(tb.first, tb.second.id), "$startId/$lastDbId", profile)
}
return
} else {
aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryBasal. No profile: ${tb.second.id} ")
confirmLastTemporaryBasalIdIfGreater(tb.second.id)
processChangedTemporaryBasals()
return
}
}
}
override fun confirmLastExtendedBolusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting ExtendedBolus data sync from $lastSynced")
sp.putLong(R.string.key_ns_extended_bolus_last_synced_id, lastSynced)
}
}
override tailrec suspend fun processChangedExtendedBoluses() {
if (isPaused) return
val lastDbId = appRepository.getLastExtendedBolusId() ?: 0L
var startId = sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_extended_bolus_last_synced_id, 0)
startId = 0
}
queueCounter.ebsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementExtendedBolus(startId).blockingGet()?.let { eb ->
aapsLogger.info(LTag.NSCLIENT, "Loading ExtendedBolus data Start: $startId ${eb.first} forID: ${eb.second.id} ")
val profile = profileFunction.getProfile(eb.first.timestamp)
if (profile != null) {
when {
// new record with existing NS id => must be coming from NS => ignore
eb.first.id == eb.second.id && eb.first.interfaceIDs.nightscoutId != null -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring ExtendedBolus. Loaded from NS: ${eb.second.id} ")
confirmLastExtendedBolusIdIfGreater(eb.second.id)
processChangedExtendedBoluses()
return
}
// only NsId changed, no need to upload
eb.first.onlyNsIdAdded(eb.second) -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring ExtendedBolus. Only NS id changed ID: ${eb.second.id} ")
confirmLastExtendedBolusIdIfGreater(eb.second.id)
processChangedExtendedBoluses()
return
}
// without nsId = create new
eb.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairExtendedBolus(eb.first, eb.second.id), "$startId/$lastDbId", profile)
// with nsId = update
eb.first.interfaceIDs.nightscoutId != null ->
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairExtendedBolus(eb.first, eb.second.id), "$startId/$lastDbId", profile)
}
return
} else {
aapsLogger.info(LTag.NSCLIENT, "Ignoring ExtendedBolus. No profile: ${eb.second.id} ")
confirmLastExtendedBolusIdIfGreater(eb.second.id)
processChangedExtendedBoluses()
return
}
}
}
override fun confirmLastProfileSwitchIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting ProfileSwitch data sync from $lastSynced")
sp.putLong(R.string.key_ns_profile_switch_last_synced_id, lastSynced)
}
}
override tailrec suspend fun processChangedProfileSwitches() {
if (isPaused) return
val lastDbId = appRepository.getLastProfileSwitchId() ?: 0L
var startId = sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_profile_switch_last_synced_id, 0)
startId = 0
}
queueCounter.pssRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementProfileSwitch(startId).blockingGet()?.let { ps ->
aapsLogger.info(LTag.NSCLIENT, "Loading ProfileSwitch data Start: $startId ${ps.first} forID: ${ps.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
ps.first.id == ps.second.id && ps.first.interfaceIDs.nightscoutId != null -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring ProfileSwitch. Loaded from NS: ${ps.second.id} ")
confirmLastProfileSwitchIdIfGreater(ps.second.id)
processChangedProfileSwitches()
return
}
// only NsId changed, no need to upload
ps.first.onlyNsIdAdded(ps.second) -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring ProfileSwitch. Only NS id changed ID: ${ps.second.id} ")
confirmLastProfileSwitchIdIfGreater(ps.second.id)
processChangedProfileSwitches()
return
}
// without nsId = create new
ps.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairProfileSwitch(ps.first, ps.second.id), "$startId/$lastDbId")
// with nsId = update
ps.first.interfaceIDs.nightscoutId != null ->
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairProfileSwitch(ps.first, ps.second.id), "$startId/$lastDbId")
}
return
}
}
override fun confirmLastEffectiveProfileSwitchIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting EffectiveProfileSwitch data sync from $lastSynced")
sp.putLong(R.string.key_ns_effective_profile_switch_last_synced_id, lastSynced)
}
}
override tailrec suspend fun processChangedEffectiveProfileSwitches() {
if (isPaused) return
val lastDbId = appRepository.getLastEffectiveProfileSwitchId() ?: 0L
var startId = sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
startId = 0
}
queueCounter.epssRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementEffectiveProfileSwitch(startId).blockingGet()?.let { ps ->
aapsLogger.info(LTag.NSCLIENT, "Loading EffectiveProfileSwitch data Start: $startId ${ps.first} forID: ${ps.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
ps.first.id == ps.second.id && ps.first.interfaceIDs.nightscoutId != null -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring EffectiveProfileSwitch. Loaded from NS: ${ps.second.id} ")
confirmLastEffectiveProfileSwitchIdIfGreater(ps.second.id)
processChangedEffectiveProfileSwitches()
return
}
// only NsId changed, no need to upload
ps.first.onlyNsIdAdded(ps.second) -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring EffectiveProfileSwitch. Only NS id changed ID: ${ps.second.id} ")
confirmLastEffectiveProfileSwitchIdIfGreater(ps.second.id)
processChangedEffectiveProfileSwitches()
return
}
// without nsId = create new
ps.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairEffectiveProfileSwitch(ps.first, ps.second.id), "$startId/$lastDbId")
// with nsId = update
ps.first.interfaceIDs.nightscoutId != null ->
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairEffectiveProfileSwitch(ps.first, ps.second.id), "$startId/$lastDbId")
}
return
}
}
override fun confirmLastOfflineEventIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting OfflineEvent data sync from $lastSynced")
sp.putLong(R.string.key_ns_offline_event_last_synced_id, lastSynced)
}
}
override tailrec suspend fun processChangedOfflineEvents() {
if (isPaused) return
val lastDbId = appRepository.getLastOfflineEventId() ?: 0L
var startId = sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_offline_event_last_synced_id, 0)
startId = 0
}
queueCounter.oesRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementOfflineEvent(startId).blockingGet()?.let { oe ->
aapsLogger.info(LTag.NSCLIENT, "Loading OfflineEvent data Start: $startId ${oe.first} forID: ${oe.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
oe.first.id == oe.second.id && oe.first.interfaceIDs.nightscoutId != null -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring OfflineEvent. Loaded from NS: ${oe.second.id} ")
confirmLastOfflineEventIdIfGreater(oe.second.id)
processChangedOfflineEvents()
return
}
// only NsId changed, no need to upload
oe.first.onlyNsIdAdded(oe.second) -> {
aapsLogger.info(LTag.NSCLIENT, "Ignoring OfflineEvent. Only NS id changed ID: ${oe.second.id} ")
confirmLastOfflineEventIdIfGreater(oe.second.id)
processChangedOfflineEvents()
return
}
// without nsId = create new
oe.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairOfflineEvent(oe.first, oe.second.id), "$startId/$lastDbId")
// existing with nsId = update
oe.first.interfaceIDs.nightscoutId != null ->
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairOfflineEvent(oe.first, oe.second.id), "$startId/$lastDbId")
}
return
}
}
override fun confirmLastProfileStore(lastSynced: Long) {
sp.putLong(R.string.key_ns_profile_store_last_synced_timestamp, lastSynced)
}
override suspend fun processChangedProfileStore() {
if (isPaused) return
val lastSync = sp.getLong(R.string.key_ns_profile_store_last_synced_timestamp, 0)
val lastChange = sp.getLong(info.nightscout.core.utils.R.string.key_local_profile_last_change, 0)
if (lastChange == 0L) return
if (lastChange > lastSync) {
if (activePlugin.activeProfileSource.profile?.allProfilesValid != true) return
val profileStore = activePlugin.activeProfileSource.profile
val profileJson = profileStore?.data ?: return
// add for v3
if (JsonHelper.safeGetLongAllowNull(profileJson, "date") == null)
profileJson.put("date", profileStore.getStartDate())
activePlugin.activeNsClient?.nsAdd("profile", DataSyncSelector.PairProfileStore(profileJson, dateUtil.now()), "")
}
}
}

View file

@ -5,7 +5,6 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.ServiceConnection import android.content.ServiceConnection
import android.os.IBinder import android.os.IBinder
import android.provider.ContactsContract
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
@ -24,7 +23,6 @@ import info.nightscout.interfaces.profile.Profile
import info.nightscout.interfaces.profile.ProfileFunction import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.source.DoingOwnUploadSource import info.nightscout.interfaces.source.DoingOwnUploadSource
import info.nightscout.interfaces.sync.DataSyncSelector import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.DataSyncSelectorV1
import info.nightscout.interfaces.sync.NsClient import info.nightscout.interfaces.sync.NsClient
import info.nightscout.interfaces.sync.Sync import info.nightscout.interfaces.sync.Sync
import info.nightscout.plugins.sync.R import info.nightscout.plugins.sync.R

View file

@ -25,7 +25,6 @@ import info.nightscout.interfaces.notifications.Notification
import info.nightscout.interfaces.nsclient.NSAlarm import info.nightscout.interfaces.nsclient.NSAlarm
import info.nightscout.interfaces.nsclient.NSSettingsStatus import info.nightscout.interfaces.nsclient.NSSettingsStatus
import info.nightscout.interfaces.nsclient.StoreDataForDb import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.sync.DataSyncSelectorV1
import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.utils.JsonHelper.safeGetString import info.nightscout.interfaces.utils.JsonHelper.safeGetString
import info.nightscout.interfaces.utils.JsonHelper.safeGetStringAllowNull import info.nightscout.interfaces.utils.JsonHelper.safeGetStringAllowNull
@ -34,6 +33,7 @@ import info.nightscout.plugins.sync.nsShared.NsIncomingDataProcessor
import info.nightscout.plugins.sync.nsShared.events.EventConnectivityOptionChanged import info.nightscout.plugins.sync.nsShared.events.EventConnectivityOptionChanged
import info.nightscout.plugins.sync.nsShared.events.EventNSClientStatus import info.nightscout.plugins.sync.nsShared.events.EventNSClientStatus
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiStatus import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiStatus
import info.nightscout.plugins.sync.nsclient.DataSyncSelectorV1
import info.nightscout.plugins.sync.nsclient.NSClientPlugin import info.nightscout.plugins.sync.nsclient.NSClientPlugin
import info.nightscout.plugins.sync.nsclient.acks.NSAddAck import info.nightscout.plugins.sync.nsclient.acks.NSAddAck
import info.nightscout.plugins.sync.nsclient.acks.NSAuthAck import info.nightscout.plugins.sync.nsclient.acks.NSAuthAck
@ -179,6 +179,18 @@ import javax.inject.Inject
.toObservable(EventNewHistoryData::class.java) .toObservable(EventNewHistoryData::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ resend("NEW_DATA") }, fabricPrivacy::logException) .subscribe({ resend("NEW_DATA") }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventDeviceStatusChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ resend("EventDeviceStatusChange") }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTherapyEventChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ resend("EventTherapyEventChange") }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventOfflineChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ resend("EventOfflineChange") }, fabricPrivacy::logException)
} }
override fun onDestroy() { override fun onDestroy() {
@ -541,6 +553,7 @@ import javax.inject.Inject
} }
} }
rxBus.send(EventNSClientNewLog("◄ LAST", dateUtil.dateAndTimeString(latestDateInReceivedData))) rxBus.send(EventNSClientNewLog("◄ LAST", dateUtil.dateAndTimeString(latestDateInReceivedData)))
resend("LAST")
} catch (e: JSONException) { } catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e) aapsLogger.error("Unhandled exception", e)
} }
@ -551,7 +564,7 @@ import javax.inject.Inject
} }
} }
fun dbUpdate(collection: String, _id: String?, data: JSONObject?, originalObject: Any, progress: String) { fun dbUpdate(collection: String, @Suppress("LocalVariableName") _id: String?, data: JSONObject?, originalObject: Any, progress: String) {
try { try {
if (_id == null) return if (_id == null) return
if (!isConnected || !hasWriteAuth) return if (!isConnected || !hasWriteAuth) return
@ -595,10 +608,10 @@ import javax.inject.Inject
if (!isConnected || !hasWriteAuth) return@runBlocking if (!isConnected || !hasWriteAuth) return@runBlocking
scope.async { scope.async {
if (socket?.connected() != true) return@async if (socket?.connected() != true) return@async
if (lastAckTime > System.currentTimeMillis() - 10 * 1000L) { // if (lastAckTime > System.currentTimeMillis() - 10 * 1000L) {
aapsLogger.debug(LTag.NSCLIENT, "Skipping resend by lastAckTime: " + (System.currentTimeMillis() - lastAckTime) / 1000L + " sec") // aapsLogger.debug(LTag.NSCLIENT, "Skipping resend by lastAckTime: " + (System.currentTimeMillis() - lastAckTime) / 1000L + " sec")
return@async // return@async
} // }
rxBus.send(EventNSClientNewLog("● QUEUE", "Resend started: $reason")) rxBus.send(EventNSClientNewLog("● QUEUE", "Resend started: $reason"))
dataSyncSelectorV1.doUpload() dataSyncSelectorV1.doUpload()
rxBus.send(EventNSClientNewLog("● QUEUE", "Resend ended: $reason")) rxBus.send(EventNSClientNewLog("● QUEUE", "Resend ended: $reason"))

View file

@ -4,6 +4,7 @@ import android.content.Context
import android.os.SystemClock import android.os.SystemClock
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import androidx.work.workDataOf import androidx.work.workDataOf
import info.nightscout.core.utils.notify
import info.nightscout.core.utils.receivers.DataWorkerStorage import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.database.impl.AppRepository import info.nightscout.database.impl.AppRepository
@ -22,7 +23,6 @@ import info.nightscout.interfaces.sync.DataSyncSelector.PairProfileSwitch
import info.nightscout.interfaces.sync.DataSyncSelector.PairTemporaryBasal import info.nightscout.interfaces.sync.DataSyncSelector.PairTemporaryBasal
import info.nightscout.interfaces.sync.DataSyncSelector.PairTemporaryTarget import info.nightscout.interfaces.sync.DataSyncSelector.PairTemporaryTarget
import info.nightscout.interfaces.sync.DataSyncSelector.PairTherapyEvent import info.nightscout.interfaces.sync.DataSyncSelector.PairTherapyEvent
import info.nightscout.interfaces.sync.DataSyncSelectorV1
import info.nightscout.plugins.sync.R import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsclient.acks.NSAddAck import info.nightscout.plugins.sync.nsclient.acks.NSAddAck
import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.AapsSchedulers
@ -40,7 +40,6 @@ class NSClientAddAckWorker(
@Inject lateinit var dataWorkerStorage: DataWorkerStorage @Inject lateinit var dataWorkerStorage: DataWorkerStorage
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var rxBus: RxBus @Inject lateinit var rxBus: RxBus
@Inject lateinit var dataSyncSelectorV1: DataSyncSelectorV1
@Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var storeDataForDb: StoreDataForDb @Inject lateinit var storeDataForDb: StoreDataForDb
@ -60,152 +59,128 @@ class NSClientAddAckWorker(
is PairTemporaryTarget -> { is PairTemporaryTarget -> {
val pair = ack.originalObject val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id pair.value.interfaceIDs.nightscoutId = ack.id
pair.confirmed = true
storeDataForDb.nsIdTemporaryTargets.add(pair.value) storeDataForDb.nsIdTemporaryTargets.add(pair.value)
dataSyncSelectorV1.confirmLastTempTargetsIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate() storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked TemporaryTarget " + pair.value.interfaceIDs.nightscoutId)) rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked TemporaryTarget " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelectorV1.processChangedTempTargets()
} }
is PairGlucoseValue -> { is PairGlucoseValue -> {
val pair = ack.originalObject val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id pair.value.interfaceIDs.nightscoutId = ack.id
pair.confirmed = true
storeDataForDb.nsIdGlucoseValues.add(pair.value) storeDataForDb.nsIdGlucoseValues.add(pair.value)
dataSyncSelectorV1.confirmLastGlucoseValueIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate() storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked GlucoseValue " + pair.value.interfaceIDs.nightscoutId)) rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked GlucoseValue " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelectorV1.processChangedGlucoseValues()
} }
is PairFood -> { is PairFood -> {
val pair = ack.originalObject val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id pair.value.interfaceIDs.nightscoutId = ack.id
pair.confirmed = true
storeDataForDb.nsIdFoods.add(pair.value) storeDataForDb.nsIdFoods.add(pair.value)
dataSyncSelectorV1.confirmLastFoodIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate() storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked Food " + pair.value.interfaceIDs.nightscoutId)) rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked Food " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting // Send new if waiting
dataSyncSelectorV1.processChangedFoods()
} }
is PairTherapyEvent -> { is PairTherapyEvent -> {
val pair = ack.originalObject val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id pair.value.interfaceIDs.nightscoutId = ack.id
pair.confirmed = true
storeDataForDb.nsIdTherapyEvents.add(pair.value) storeDataForDb.nsIdTherapyEvents.add(pair.value)
dataSyncSelectorV1.confirmLastTherapyEventIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate() storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked TherapyEvent " + pair.value.interfaceIDs.nightscoutId)) rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked TherapyEvent " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelectorV1.processChangedTherapyEvents()
} }
is PairBolus -> { is PairBolus -> {
val pair = ack.originalObject val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id pair.value.interfaceIDs.nightscoutId = ack.id
pair.confirmed = true
storeDataForDb.nsIdBoluses.add(pair.value) storeDataForDb.nsIdBoluses.add(pair.value)
dataSyncSelectorV1.confirmLastBolusIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate() storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked Bolus " + pair.value.interfaceIDs.nightscoutId)) rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked Bolus " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelectorV1.processChangedBoluses()
} }
is PairCarbs -> { is PairCarbs -> {
val pair = ack.originalObject val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id pair.value.interfaceIDs.nightscoutId = ack.id
pair.confirmed = true
storeDataForDb.nsIdCarbs.add(pair.value) storeDataForDb.nsIdCarbs.add(pair.value)
dataSyncSelectorV1.confirmLastCarbsIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate() storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked Carbs " + pair.value.interfaceIDs.nightscoutId)) rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked Carbs " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelectorV1.processChangedCarbs()
} }
is PairBolusCalculatorResult -> { is PairBolusCalculatorResult -> {
val pair = ack.originalObject val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdBolusCalculatorResults.add(pair.value) storeDataForDb.nsIdBolusCalculatorResults.add(pair.value)
dataSyncSelectorV1.confirmLastBolusCalculatorResultsIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate() storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked BolusCalculatorResult " + pair.value.interfaceIDs.nightscoutId)) rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked BolusCalculatorResult " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelectorV1.processChangedBolusCalculatorResults()
} }
is PairTemporaryBasal -> { is PairTemporaryBasal -> {
val pair = ack.originalObject val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id pair.value.interfaceIDs.nightscoutId = ack.id
pair.confirmed = true
storeDataForDb.nsIdTemporaryBasals.add(pair.value) storeDataForDb.nsIdTemporaryBasals.add(pair.value)
dataSyncSelectorV1.confirmLastTemporaryBasalIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate() storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked TemporaryBasal " + pair.value.interfaceIDs.nightscoutId)) rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked TemporaryBasal " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelectorV1.processChangedTemporaryBasals()
} }
is PairExtendedBolus -> { is PairExtendedBolus -> {
val pair = ack.originalObject val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id pair.value.interfaceIDs.nightscoutId = ack.id
pair.confirmed = true
storeDataForDb.nsIdExtendedBoluses.add(pair.value) storeDataForDb.nsIdExtendedBoluses.add(pair.value)
dataSyncSelectorV1.confirmLastExtendedBolusIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate() storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked ExtendedBolus " + pair.value.interfaceIDs.nightscoutId)) rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked ExtendedBolus " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelectorV1.processChangedExtendedBoluses()
} }
is PairProfileSwitch -> { is PairProfileSwitch -> {
val pair = ack.originalObject val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id pair.value.interfaceIDs.nightscoutId = ack.id
pair.confirmed = true
storeDataForDb.nsIdProfileSwitches.add(pair.value) storeDataForDb.nsIdProfileSwitches.add(pair.value)
dataSyncSelectorV1.confirmLastProfileSwitchIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate() storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked ProfileSwitch " + pair.value.interfaceIDs.nightscoutId)) rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked ProfileSwitch " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelectorV1.processChangedProfileSwitches()
} }
is PairEffectiveProfileSwitch -> { is PairEffectiveProfileSwitch -> {
val pair = ack.originalObject val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id pair.value.interfaceIDs.nightscoutId = ack.id
pair.confirmed = true
storeDataForDb.nsIdEffectiveProfileSwitches.add(pair.value) storeDataForDb.nsIdEffectiveProfileSwitches.add(pair.value)
dataSyncSelectorV1.confirmLastEffectiveProfileSwitchIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate() storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked EffectiveProfileSwitch " + pair.value.interfaceIDs.nightscoutId)) rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked EffectiveProfileSwitch " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelectorV1.processChangedEffectiveProfileSwitches()
} }
is DataSyncSelector.PairDeviceStatus -> { is DataSyncSelector.PairDeviceStatus -> {
val pair = ack.originalObject val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id pair.value.interfaceIDs.nightscoutId = ack.id
pair.confirmed = true
storeDataForDb.nsIdDeviceStatuses.add(pair.value) storeDataForDb.nsIdDeviceStatuses.add(pair.value)
dataSyncSelectorV1.confirmLastDeviceStatusIdIfGreater(pair.value.id)
storeDataForDb.scheduleNsIdUpdate() storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked DeviceStatus " + pair.value.interfaceIDs.nightscoutId)) rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked DeviceStatus " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelectorV1.processChangedDeviceStatuses()
} }
is PairProfileStore -> { is PairProfileStore -> {
dataSyncSelectorV1.confirmLastProfileStore(ack.originalObject.id) val pair = ack.originalObject
pair.confirmed = true
rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked ProfileStore " + ack.id)) rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked ProfileStore " + ack.id))
} }
is PairOfflineEvent -> { is PairOfflineEvent -> {
val pair = ack.originalObject val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id pair.value.interfaceIDs.nightscoutId = ack.id
pair.confirmed = true
storeDataForDb.nsIdOfflineEvents.add(pair.value) storeDataForDb.nsIdOfflineEvents.add(pair.value)
dataSyncSelectorV1.confirmLastOfflineEventIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate() storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked OfflineEvent " + pair.value.interfaceIDs.nightscoutId)) rxBus.send(EventNSClientNewLog("◄ DBADD", "Acked OfflineEvent " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelectorV1.processChangedOfflineEvents()
} }
} }
ack.originalObject?.let { synchronized(it) { it.notify() } }
return ret return ret
} }
} }

View file

@ -3,6 +3,7 @@ package info.nightscout.plugins.sync.nsclient.workers
import android.content.Context import android.content.Context
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import androidx.work.workDataOf import androidx.work.workDataOf
import info.nightscout.core.utils.notify
import info.nightscout.core.utils.receivers.DataWorkerStorage import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.database.impl.AppRepository import info.nightscout.database.impl.AppRepository
@ -18,7 +19,6 @@ import info.nightscout.interfaces.sync.DataSyncSelector.PairProfileSwitch
import info.nightscout.interfaces.sync.DataSyncSelector.PairTemporaryBasal import info.nightscout.interfaces.sync.DataSyncSelector.PairTemporaryBasal
import info.nightscout.interfaces.sync.DataSyncSelector.PairTemporaryTarget import info.nightscout.interfaces.sync.DataSyncSelector.PairTemporaryTarget
import info.nightscout.interfaces.sync.DataSyncSelector.PairTherapyEvent import info.nightscout.interfaces.sync.DataSyncSelector.PairTherapyEvent
import info.nightscout.interfaces.sync.DataSyncSelectorV1
import info.nightscout.plugins.sync.nsclient.acks.NSUpdateAck import info.nightscout.plugins.sync.nsclient.acks.NSUpdateAck
import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
@ -34,7 +34,6 @@ class NSClientUpdateRemoveAckWorker(
@Inject lateinit var dataWorkerStorage: DataWorkerStorage @Inject lateinit var dataWorkerStorage: DataWorkerStorage
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var rxBus: RxBus @Inject lateinit var rxBus: RxBus
@Inject lateinit var dataSyncSelectorV1: DataSyncSelectorV1
@Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var aapsSchedulers: AapsSchedulers
override suspend fun doWorkAndLog(): Result { override suspend fun doWorkAndLog(): Result {
@ -47,112 +46,89 @@ class NSClientUpdateRemoveAckWorker(
when (ack.originalObject) { when (ack.originalObject) {
is PairTemporaryTarget -> { is PairTemporaryTarget -> {
val pair = ack.originalObject val pair = ack.originalObject
dataSyncSelectorV1.confirmLastTempTargetsIdIfGreater(pair.id) pair.confirmed = true
rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked TemporaryTarget" + ack._id)) rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked TemporaryTarget" + ack._id))
// Send new if waiting
dataSyncSelectorV1.processChangedTempTargets()
ret = Result.success(workDataOf("ProcessedData" to pair.toString())) ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
} }
is PairGlucoseValue -> { is PairGlucoseValue -> {
val pair = ack.originalObject val pair = ack.originalObject
dataSyncSelectorV1.confirmLastGlucoseValueIdIfGreater(pair.id) pair.confirmed = true
rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked GlucoseValue " + ack._id)) rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked GlucoseValue " + ack._id))
// Send new if waiting
dataSyncSelectorV1.processChangedGlucoseValues()
ret = Result.success(workDataOf("ProcessedData" to pair.toString())) ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
} }
is PairFood -> { is PairFood -> {
val pair = ack.originalObject val pair = ack.originalObject
dataSyncSelectorV1.confirmLastFoodIdIfGreater(pair.id) pair.confirmed = true
rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked Food " + ack._id)) rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked Food " + ack._id))
// Send new if waiting
dataSyncSelectorV1.processChangedFoods()
ret = Result.success(workDataOf("ProcessedData" to pair.toString())) ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
} }
is PairTherapyEvent -> { is PairTherapyEvent -> {
val pair = ack.originalObject val pair = ack.originalObject
dataSyncSelectorV1.confirmLastTherapyEventIdIfGreater(pair.id) pair.confirmed = true
rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked TherapyEvent " + ack._id)) rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked TherapyEvent " + ack._id))
// Send new if waiting
dataSyncSelectorV1.processChangedTherapyEvents()
ret = Result.success(workDataOf("ProcessedData" to pair.toString())) ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
} }
is PairBolus -> { is PairBolus -> {
val pair = ack.originalObject val pair = ack.originalObject
dataSyncSelectorV1.confirmLastBolusIdIfGreater(pair.id) pair.confirmed = true
rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked Bolus " + ack._id)) rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked Bolus " + ack._id))
// Send new if waiting
dataSyncSelectorV1.processChangedBoluses()
ret = Result.success(workDataOf("ProcessedData" to pair.toString())) ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
} }
is PairCarbs -> { is PairCarbs -> {
val pair = ack.originalObject val pair = ack.originalObject
dataSyncSelectorV1.confirmLastCarbsIdIfGreater(pair.id) pair.confirmed = true
rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked Carbs " + ack._id)) rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked Carbs " + ack._id))
// Send new if waiting
dataSyncSelectorV1.processChangedCarbs()
ret = Result.success(workDataOf("ProcessedData" to pair.toString())) ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
} }
is PairBolusCalculatorResult -> { is PairBolusCalculatorResult -> {
val pair = ack.originalObject val pair = ack.originalObject
dataSyncSelectorV1.confirmLastBolusCalculatorResultsIdIfGreater(pair.id) pair.confirmed = true
rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked BolusCalculatorResult " + ack._id)) rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked BolusCalculatorResult " + ack._id))
// Send new if waiting
dataSyncSelectorV1.processChangedBolusCalculatorResults()
ret = Result.success(workDataOf("ProcessedData" to pair.toString())) ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
} }
is PairTemporaryBasal -> { is PairTemporaryBasal -> {
val pair = ack.originalObject val pair = ack.originalObject
dataSyncSelectorV1.confirmLastTemporaryBasalIdIfGreater(pair.id) pair.confirmed = true
rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked TemporaryBasal " + ack._id)) rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked TemporaryBasal " + ack._id))
// Send new if waiting
dataSyncSelectorV1.processChangedTemporaryBasals()
ret = Result.success(workDataOf("ProcessedData" to pair.toString())) ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
} }
is PairExtendedBolus -> { is PairExtendedBolus -> {
val pair = ack.originalObject val pair = ack.originalObject
dataSyncSelectorV1.confirmLastExtendedBolusIdIfGreater(pair.id) pair.confirmed = true
rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked ExtendedBolus " + ack._id)) rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked ExtendedBolus " + ack._id))
// Send new if waiting
dataSyncSelectorV1.processChangedExtendedBoluses()
ret = Result.success(workDataOf("ProcessedData" to pair.toString())) ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
} }
is PairProfileSwitch -> { is PairProfileSwitch -> {
val pair = ack.originalObject val pair = ack.originalObject
dataSyncSelectorV1.confirmLastProfileSwitchIdIfGreater(pair.id) pair.confirmed = true
rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked ProfileSwitch " + ack._id)) rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked ProfileSwitch " + ack._id))
// Send new if waiting
dataSyncSelectorV1.processChangedProfileSwitches()
ret = Result.success(workDataOf("ProcessedData" to pair.toString())) ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
} }
is PairEffectiveProfileSwitch -> { is PairEffectiveProfileSwitch -> {
val pair = ack.originalObject val pair = ack.originalObject
dataSyncSelectorV1.confirmLastEffectiveProfileSwitchIdIfGreater(pair.id) pair.confirmed = true
rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked EffectiveProfileSwitch " + ack._id)) rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked EffectiveProfileSwitch " + ack._id))
// Send new if waiting
dataSyncSelectorV1.processChangedEffectiveProfileSwitches()
ret = Result.success(workDataOf("ProcessedData" to pair.toString())) ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
} }
is PairOfflineEvent -> { is PairOfflineEvent -> {
val pair = ack.originalObject val pair = ack.originalObject
dataSyncSelectorV1.confirmLastOfflineEventIdIfGreater(pair.id) pair.confirmed = true
rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked OfflineEvent" + ack._id)) rxBus.send(EventNSClientNewLog("◄ DBUPDATE", "Acked OfflineEvent" + ack._id))
// Send new if waiting
dataSyncSelectorV1.processChangedOfflineEvents()
ret = Result.success(workDataOf("ProcessedData" to pair.toString())) ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
} }
} }
ack.originalObject?.let { synchronized(it) { it.notify() } }
return ret return ret
} }
} }

View file

@ -7,7 +7,6 @@ import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.ProfileFunction import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.sync.DataSyncSelector import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.DataSyncSelectorV3
import info.nightscout.interfaces.utils.JsonHelper import info.nightscout.interfaces.utils.JsonHelper
import info.nightscout.plugins.sync.R import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiQueue import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiQueue
@ -22,7 +21,7 @@ import javax.inject.Singleton
@OpenForTesting @OpenForTesting
@Singleton @Singleton
class DataSyncSelectorV3Impl @Inject constructor( class DataSyncSelectorV3 @Inject constructor(
private val sp: SP, private val sp: SP,
private val aapsLogger: AAPSLogger, private val aapsLogger: AAPSLogger,
private val dateUtil: DateUtil, private val dateUtil: DateUtil,
@ -32,7 +31,7 @@ class DataSyncSelectorV3Impl @Inject constructor(
private val rxBus: RxBus, private val rxBus: RxBus,
private val storeDataForDb: StoreDataForDb, private val storeDataForDb: StoreDataForDb,
private val config: Config private val config: Config
) : DataSyncSelectorV3 { ) : DataSyncSelector {
class QueueCounter( class QueueCounter(
var bolusesRemaining: Long = -1L, var bolusesRemaining: Long = -1L,
@ -127,14 +126,14 @@ class DataSyncSelectorV3Impl @Inject constructor(
else sp.remove(R.string.key_ns_device_status_last_synced_id) else sp.remove(R.string.key_ns_device_status_last_synced_id)
} }
fun confirmLastBolusIdIfGreater(lastSynced: Long) { private fun confirmLastBolusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)) { if (lastSynced > sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting Bolus data sync from $lastSynced") //aapsLogger.debug(LTag.NSCLIENT, "Setting Bolus data sync from $lastSynced")
sp.putLong(R.string.key_ns_bolus_last_synced_id, lastSynced) sp.putLong(R.string.key_ns_bolus_last_synced_id, lastSynced)
} }
} }
suspend fun processChangedBoluses() { private suspend fun processChangedBoluses() {
var cont = true var cont = true
while (cont) { while (cont) {
if (isPaused) return if (isPaused) return
@ -170,14 +169,14 @@ class DataSyncSelectorV3Impl @Inject constructor(
} }
} }
fun confirmLastCarbsIdIfGreater(lastSynced: Long) { private fun confirmLastCarbsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)) { if (lastSynced > sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting Carbs data sync from $lastSynced") //aapsLogger.debug(LTag.NSCLIENT, "Setting Carbs data sync from $lastSynced")
sp.putLong(R.string.key_ns_carbs_last_synced_id, lastSynced) sp.putLong(R.string.key_ns_carbs_last_synced_id, lastSynced)
} }
} }
suspend fun processChangedCarbs() { private suspend fun processChangedCarbs() {
var cont = true var cont = true
while (cont) { while (cont) {
if (isPaused) return if (isPaused) return
@ -213,14 +212,14 @@ class DataSyncSelectorV3Impl @Inject constructor(
} }
} }
fun confirmLastBolusCalculatorResultsIdIfGreater(lastSynced: Long) { private fun confirmLastBolusCalculatorResultsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)) { if (lastSynced > sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting BolusCalculatorResult data sync from $lastSynced") //aapsLogger.debug(LTag.NSCLIENT, "Setting BolusCalculatorResult data sync from $lastSynced")
sp.putLong(R.string.key_ns_bolus_calculator_result_last_synced_id, lastSynced) sp.putLong(R.string.key_ns_bolus_calculator_result_last_synced_id, lastSynced)
} }
} }
suspend fun processChangedBolusCalculatorResults() { private suspend fun processChangedBolusCalculatorResults() {
var cont = true var cont = true
while (cont) { while (cont) {
if (isPaused) return if (isPaused) return
@ -264,14 +263,14 @@ class DataSyncSelectorV3Impl @Inject constructor(
} }
} }
fun confirmLastTempTargetsIdIfGreater(lastSynced: Long) { private fun confirmLastTempTargetsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)) { if (lastSynced > sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting TemporaryTarget data sync from $lastSynced") //aapsLogger.debug(LTag.NSCLIENT, "Setting TemporaryTarget data sync from $lastSynced")
sp.putLong(R.string.key_ns_temporary_target_last_synced_id, lastSynced) sp.putLong(R.string.key_ns_temporary_target_last_synced_id, lastSynced)
} }
} }
suspend fun processChangedTempTargets() { private suspend fun processChangedTempTargets() {
var cont = true var cont = true
while (cont) { while (cont) {
if (isPaused) return if (isPaused) return
@ -307,14 +306,14 @@ class DataSyncSelectorV3Impl @Inject constructor(
} }
} }
fun confirmLastFoodIdIfGreater(lastSynced: Long) { private fun confirmLastFoodIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_food_last_synced_id, 0)) { if (lastSynced > sp.getLong(R.string.key_ns_food_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting Food data sync from $lastSynced") //aapsLogger.debug(LTag.NSCLIENT, "Setting Food data sync from $lastSynced")
sp.putLong(R.string.key_ns_food_last_synced_id, lastSynced) sp.putLong(R.string.key_ns_food_last_synced_id, lastSynced)
} }
} }
suspend fun processChangedFoods() { private suspend fun processChangedFoods() {
var cont = true var cont = true
while (cont) { while (cont) {
if (isPaused) return if (isPaused) return
@ -350,14 +349,14 @@ class DataSyncSelectorV3Impl @Inject constructor(
} }
} }
fun confirmLastGlucoseValueIdIfGreater(lastSynced: Long) { private fun confirmLastGlucoseValueIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)) { if (lastSynced > sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting GlucoseValue data sync from $lastSynced") //aapsLogger.debug(LTag.NSCLIENT, "Setting GlucoseValue data sync from $lastSynced")
sp.putLong(R.string.key_ns_glucose_value_last_synced_id, lastSynced) sp.putLong(R.string.key_ns_glucose_value_last_synced_id, lastSynced)
} }
} }
suspend fun processChangedGlucoseValues() { private suspend fun processChangedGlucoseValues() {
var cont = true var cont = true
while (cont) { while (cont) {
if (isPaused) return if (isPaused) return
@ -395,14 +394,14 @@ class DataSyncSelectorV3Impl @Inject constructor(
} }
} }
fun confirmLastTherapyEventIdIfGreater(lastSynced: Long) { private fun confirmLastTherapyEventIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)) { if (lastSynced > sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting TherapyEvents data sync from $lastSynced") //aapsLogger.debug(LTag.NSCLIENT, "Setting TherapyEvents data sync from $lastSynced")
sp.putLong(R.string.key_ns_therapy_event_last_synced_id, lastSynced) sp.putLong(R.string.key_ns_therapy_event_last_synced_id, lastSynced)
} }
} }
suspend fun processChangedTherapyEvents() { private suspend fun processChangedTherapyEvents() {
var cont = true var cont = true
while (cont) { while (cont) {
if (isPaused) return if (isPaused) return
@ -438,44 +437,45 @@ class DataSyncSelectorV3Impl @Inject constructor(
} }
} }
fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long) { private fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)) { if (lastSynced > sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting DeviceStatus data sync from $lastSynced") //aapsLogger.debug(LTag.NSCLIENT, "Setting DeviceStatus data sync from $lastSynced")
sp.putLong(R.string.key_ns_device_status_last_synced_id, lastSynced) sp.putLong(R.string.key_ns_device_status_last_synced_id, lastSynced)
} }
} }
suspend fun processChangedDeviceStatuses() { private suspend fun processChangedDeviceStatuses() {
if (isPaused) return var cont = true
val lastDbId = appRepository.getLastDeviceStatusId() ?: 0L while (cont) {
var startId = sp.getLong(R.string.key_ns_device_status_last_synced_id, 0) if (isPaused) return
if (startId > lastDbId) { val lastDbId = appRepository.getLastDeviceStatusId() ?: 0L
aapsLogger.info(LTag.NSCLIENT, "Resetting startId: $startId lastDbId: $lastDbId") var startId = sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)
sp.putLong(R.string.key_ns_device_status_last_synced_id, 0) if (startId > lastDbId) {
startId = 0 aapsLogger.info(LTag.NSCLIENT, "Resetting startId: $startId lastDbId: $lastDbId")
} sp.putLong(R.string.key_ns_device_status_last_synced_id, 0)
queueCounter.dssRemaining = lastDbId - startId startId = 0
rxBus.send(EventNSClientUpdateGuiQueue()) }
appRepository.getNextSyncElementDeviceStatus(startId).blockingGet()?.let { deviceStatus -> queueCounter.dssRemaining = lastDbId - startId
//aapsLogger.info(LTag.NSCLIENT, "Loading DeviceStatus data Start: $startId $deviceStatus") rxBus.send(EventNSClientUpdateGuiQueue())
// without nsId = create new appRepository.getNextSyncElementDeviceStatus(startId).blockingGet()?.let { deviceStatus ->
if (deviceStatus.interfaceIDs.nightscoutId == null) { //aapsLogger.info(LTag.NSCLIENT, "Loading DeviceStatus data Start: $startId $deviceStatus")
if (activePlugin.activeNsClient?.nsAdd("devicestatus", DataSyncSelector.PairDeviceStatus(deviceStatus, lastDbId), "$startId/$lastDbId") == true) cont = activePlugin.activeNsClient?.nsAdd("devicestatus", DataSyncSelector.PairDeviceStatus(deviceStatus, lastDbId), "$startId/$lastDbId") ?: false
confirmLastDeviceStatusIdIfGreater(lastDbId) if (cont) confirmLastDeviceStatusIdIfGreater(deviceStatus.id)
// with nsId = ignore
} ?: run {
cont = false
} }
// with nsId = ignore
} }
queueCounter.dssRemaining = 0
} }
fun confirmLastTemporaryBasalIdIfGreater(lastSynced: Long) { private fun confirmLastTemporaryBasalIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)) { if (lastSynced > sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting TemporaryBasal data sync from $lastSynced") //aapsLogger.debug(LTag.NSCLIENT, "Setting TemporaryBasal data sync from $lastSynced")
sp.putLong(R.string.key_ns_temporary_basal_last_synced_id, lastSynced) sp.putLong(R.string.key_ns_temporary_basal_last_synced_id, lastSynced)
} }
} }
suspend fun processChangedTemporaryBasals() { private suspend fun processChangedTemporaryBasals() {
var cont = true var cont = true
while (cont) { while (cont) {
if (isPaused) return if (isPaused) return
@ -512,14 +512,14 @@ class DataSyncSelectorV3Impl @Inject constructor(
} }
} }
fun confirmLastExtendedBolusIdIfGreater(lastSynced: Long) { private fun confirmLastExtendedBolusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)) { if (lastSynced > sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting ExtendedBolus data sync from $lastSynced") //aapsLogger.debug(LTag.NSCLIENT, "Setting ExtendedBolus data sync from $lastSynced")
sp.putLong(R.string.key_ns_extended_bolus_last_synced_id, lastSynced) sp.putLong(R.string.key_ns_extended_bolus_last_synced_id, lastSynced)
} }
} }
suspend fun processChangedExtendedBoluses() { private suspend fun processChangedExtendedBoluses() {
var cont = true var cont = true
while (cont) { while (cont) {
if (isPaused) return if (isPaused) return
@ -558,14 +558,14 @@ class DataSyncSelectorV3Impl @Inject constructor(
} }
} }
fun confirmLastProfileSwitchIdIfGreater(lastSynced: Long) { private fun confirmLastProfileSwitchIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)) { if (lastSynced > sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting ProfileSwitch data sync from $lastSynced") //aapsLogger.debug(LTag.NSCLIENT, "Setting ProfileSwitch data sync from $lastSynced")
sp.putLong(R.string.key_ns_profile_switch_last_synced_id, lastSynced) sp.putLong(R.string.key_ns_profile_switch_last_synced_id, lastSynced)
} }
} }
suspend fun processChangedProfileSwitches() { private suspend fun processChangedProfileSwitches() {
var cont = true var cont = true
while (cont) { while (cont) {
if (isPaused) return if (isPaused) return
@ -601,14 +601,14 @@ class DataSyncSelectorV3Impl @Inject constructor(
} }
} }
fun confirmLastEffectiveProfileSwitchIdIfGreater(lastSynced: Long) { private fun confirmLastEffectiveProfileSwitchIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)) { if (lastSynced > sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting EffectiveProfileSwitch data sync from $lastSynced") //aapsLogger.debug(LTag.NSCLIENT, "Setting EffectiveProfileSwitch data sync from $lastSynced")
sp.putLong(R.string.key_ns_effective_profile_switch_last_synced_id, lastSynced) sp.putLong(R.string.key_ns_effective_profile_switch_last_synced_id, lastSynced)
} }
} }
suspend fun processChangedEffectiveProfileSwitches() { private suspend fun processChangedEffectiveProfileSwitches() {
var cont = true var cont = true
while (cont) { while (cont) {
if (isPaused) return if (isPaused) return
@ -644,14 +644,14 @@ class DataSyncSelectorV3Impl @Inject constructor(
} }
} }
fun confirmLastOfflineEventIdIfGreater(lastSynced: Long) { private fun confirmLastOfflineEventIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)) { if (lastSynced > sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting OfflineEvent data sync from $lastSynced") //aapsLogger.debug(LTag.NSCLIENT, "Setting OfflineEvent data sync from $lastSynced")
sp.putLong(R.string.key_ns_offline_event_last_synced_id, lastSynced) sp.putLong(R.string.key_ns_offline_event_last_synced_id, lastSynced)
} }
} }
suspend fun processChangedOfflineEvents() { private suspend fun processChangedOfflineEvents() {
var cont = true var cont = true
while (cont) { while (cont) {
if (isPaused) return if (isPaused) return
@ -687,11 +687,11 @@ class DataSyncSelectorV3Impl @Inject constructor(
} }
} }
fun confirmLastProfileStore(lastSynced: Long) { private fun confirmLastProfileStore(lastSynced: Long) {
sp.putLong(R.string.key_ns_profile_store_last_synced_timestamp, lastSynced) sp.putLong(R.string.key_ns_profile_store_last_synced_timestamp, lastSynced)
} }
suspend fun processChangedProfileStore() { private suspend fun processChangedProfileStore() {
if (isPaused) return if (isPaused) return
val lastSync = sp.getLong(R.string.key_ns_profile_store_last_synced_timestamp, 0) val lastSync = sp.getLong(R.string.key_ns_profile_store_last_synced_timestamp, 0)
val lastChange = sp.getLong(info.nightscout.core.utils.R.string.key_local_profile_last_change, 0) val lastChange = sp.getLong(info.nightscout.core.utils.R.string.key_local_profile_last_change, 0)

View file

@ -30,7 +30,6 @@ import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.profile.Profile import info.nightscout.interfaces.profile.Profile
import info.nightscout.interfaces.source.NSClientSource import info.nightscout.interfaces.source.NSClientSource
import info.nightscout.interfaces.sync.DataSyncSelector import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.DataSyncSelectorV3
import info.nightscout.interfaces.sync.NsClient import info.nightscout.interfaces.sync.NsClient
import info.nightscout.interfaces.sync.Sync import info.nightscout.interfaces.sync.Sync
import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.interfaces.ui.UiInteraction
@ -66,11 +65,14 @@ import info.nightscout.plugins.sync.nsclientV3.workers.LoadTreatmentsWorker
import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventAppExit import info.nightscout.rx.events.EventAppExit
import info.nightscout.rx.events.EventDeviceStatusChange
import info.nightscout.rx.events.EventDismissNotification import info.nightscout.rx.events.EventDismissNotification
import info.nightscout.rx.events.EventNSClientNewLog import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.rx.events.EventNewHistoryData import info.nightscout.rx.events.EventNewHistoryData
import info.nightscout.rx.events.EventOfflineChange
import info.nightscout.rx.events.EventPreferenceChange import info.nightscout.rx.events.EventPreferenceChange
import info.nightscout.rx.events.EventSWSyncStatus import info.nightscout.rx.events.EventSWSyncStatus
import info.nightscout.rx.events.EventTherapyEventChange
import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag import info.nightscout.rx.logging.LTag
import info.nightscout.sdk.NSAndroidClientImpl import info.nightscout.sdk.NSAndroidClientImpl
@ -224,6 +226,18 @@ class NSClientV3Plugin @Inject constructor(
.toObservable(EventNewHistoryData::class.java) .toObservable(EventNewHistoryData::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ executeUpload("NEW_DATA", forceNew = false) }, fabricPrivacy::logException) .subscribe({ executeUpload("NEW_DATA", forceNew = false) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventDeviceStatusChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ executeUpload("EventDeviceStatusChange", forceNew = false) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTherapyEventChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ executeUpload("EventTherapyEventChange", forceNew = false) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventOfflineChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ executeUpload("EventOfflineChange", forceNew = false) }, fabricPrivacy::logException)
runLoop = Runnable { runLoop = Runnable {
var refreshInterval = T.mins(5).msecs() var refreshInterval = T.mins(5).msecs()

View file

@ -5,7 +5,7 @@ import androidx.work.WorkerParameters
import info.nightscout.androidaps.annotations.OpenForTesting import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.core.utils.worker.LoggingWorker import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.sync.DataSyncSelectorV3 import info.nightscout.plugins.sync.nsclientV3.DataSyncSelectorV3
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog import info.nightscout.rx.events.EventNSClientNewLog

View file

@ -45,7 +45,7 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
@Mock lateinit var receiverDelegate: ReceiverDelegate @Mock lateinit var receiverDelegate: ReceiverDelegate
@Mock lateinit var uiInteraction: UiInteraction @Mock lateinit var uiInteraction: UiInteraction
@Mock lateinit var dataSyncSelectorV3: DataSyncSelectorV3Impl @Mock lateinit var dataSyncSelectorV3: DataSyncSelectorV3
@Mock lateinit var nsAndroidClient: NSAndroidClient @Mock lateinit var nsAndroidClient: NSAndroidClient
@Mock lateinit var uel: UserEntryLogger @Mock lateinit var uel: UserEntryLogger
@Mock lateinit var nsClientSource: NSClientSource @Mock lateinit var nsClientSource: NSClientSource

View file

@ -8,7 +8,7 @@ import info.nightscout.androidaps.TestBase
import info.nightscout.core.utils.fabric.FabricPrivacy import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.sync.NsClient import info.nightscout.interfaces.sync.NsClient
import info.nightscout.plugins.sync.nsclientV3.DataSyncSelectorV3Impl import info.nightscout.plugins.sync.nsclientV3.DataSyncSelectorV3
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -24,7 +24,7 @@ import org.mockito.Mockito.`when`
internal class DataSyncWorkerTest : TestBase() { internal class DataSyncWorkerTest : TestBase() {
@Mock lateinit var fabricPrivacy: FabricPrivacy @Mock lateinit var fabricPrivacy: FabricPrivacy
@Mock lateinit var dataSyncSelectorV3: DataSyncSelectorV3Impl @Mock lateinit var dataSyncSelectorV3: DataSyncSelectorV3
@Mock lateinit var activePlugin: ActivePlugin @Mock lateinit var activePlugin: ActivePlugin
@Mock lateinit var nsClient: NsClient @Mock lateinit var nsClient: NsClient
@Mock lateinit var rxBus: RxBus @Mock lateinit var rxBus: RxBus

View file

@ -17,11 +17,11 @@ import info.nightscout.interfaces.Config
import info.nightscout.interfaces.nsclient.StoreDataForDb import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.receivers.ReceiverStatusStore import info.nightscout.interfaces.receivers.ReceiverStatusStore
import info.nightscout.interfaces.source.NSClientSource import info.nightscout.interfaces.source.NSClientSource
import info.nightscout.interfaces.sync.DataSyncSelectorV3
import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.plugins.sync.nsShared.NsIncomingDataProcessor import info.nightscout.plugins.sync.nsShared.NsIncomingDataProcessor
import info.nightscout.plugins.sync.nsclient.ReceiverDelegate import info.nightscout.plugins.sync.nsclient.ReceiverDelegate
import info.nightscout.plugins.sync.nsclient.data.NSDeviceStatusHandler import info.nightscout.plugins.sync.nsclient.data.NSDeviceStatusHandler
import info.nightscout.plugins.sync.nsclientV3.DataSyncSelectorV3
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSSvgV3 import info.nightscout.plugins.sync.nsclientV3.extensions.toNSSvgV3
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus

View file

@ -1102,7 +1102,17 @@ class ComboV2Plugin @Inject constructor (
reportFinishedBolus(R.string.combov2_bolus_delivery_failed, pumpEnactResult, succeeded = false) reportFinishedBolus(R.string.combov2_bolus_delivery_failed, pumpEnactResult, succeeded = false)
} finally { } finally {
// The delivery was enacted if even a partial amount was infused. // The delivery was enacted if even a partial amount was infused.
pumpEnactResult.enacted = acquiredPump.lastBolusFlow.value?.let { it.bolusAmount > 0 } ?: false acquiredPump.lastBolusFlow.value?.also {
pumpEnactResult.enacted = (it.bolusAmount > 0)
pumpEnactResult.bolusDelivered = it.bolusAmount.cctlBolusToIU()
} ?: run {
pumpEnactResult.enacted = false
pumpEnactResult.bolusDelivered = 0.0
}
aapsLogger.debug(
LTag.PUMP,
"Pump enact result: success ${pumpEnactResult.success} enacted ${pumpEnactResult.enacted} bolusDelivered ${pumpEnactResult.bolusDelivered}"
)
bolusJob = null bolusJob = null
bolusProgressJob.cancelAndJoin() bolusProgressJob.cancelAndJoin()
} }
@ -2216,7 +2226,7 @@ class ComboV2Plugin @Inject constructor (
// only shows up in the Combo fragment. // only shows up in the Combo fragment.
if (newState == DriverState.Suspended) { if (newState == DriverState.Suspended) {
uiInteraction.addNotification( uiInteraction.addNotification(
Notification.COMBO_PUMP_SUSPENDED, Notification.PUMP_SUSPENDED,
text = rh.gs(R.string.combov2_pump_is_suspended), text = rh.gs(R.string.combov2_pump_is_suspended),
level = Notification.NORMAL level = Notification.NORMAL
) )

View file

@ -171,6 +171,7 @@ public class SerialIOThread extends Thread {
aapsLogger.error(LTag.PUMPBTCOMM, "Reply not received " + message.getMessageName()); aapsLogger.error(LTag.PUMPBTCOMM, "Reply not received " + message.getMessageName());
if (message.getCommand() == 0xF0F1) { if (message.getCommand() == 0xF0F1) {
danaPump.setNewPump(false); danaPump.setNewPump(false);
danaPump.reset();
aapsLogger.debug(LTag.PUMPBTCOMM, "Old firmware detected"); aapsLogger.debug(LTag.PUMPBTCOMM, "Old firmware detected");
} }
} }

1
pump/medtrum/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

33
pump/medtrum/build.gradle Normal file
View file

@ -0,0 +1,33 @@
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
id 'kotlin-allopen'
}
apply from: "${project.rootDir}/core/main/android_dependencies.gradle"
apply from: "${project.rootDir}/core/main/android_module_dependencies.gradle"
apply from: "${project.rootDir}/core/main/allopen_dependencies.gradle"
apply from: "${project.rootDir}/core/main/test_dependencies.gradle"
apply from: "${project.rootDir}/core/main/jacoco_global.gradle"
android {
namespace 'info.nightscout.pump.medtrum'
dataBinding {
enabled = true
}
}
dependencies {
implementation project(':app-wear-shared:shared')
implementation project(':database:entities')
implementation project(':core:libraries')
implementation project(':core:interfaces')
implementation project(':core:main')
implementation project(':core:ui')
implementation project(':core:validators')
implementation project(':pump:pump-common')
implementation project(':core:utils')
testImplementation project(':core:main')
}

View file

21
pump/medtrum/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<application>
<activity android:name=".ui.MedtrumActivity" />
<service
android:name=".services.MedtrumService"
android:enabled="true"
android:exported="false" />
</application>
</manifest>

View file

@ -0,0 +1,494 @@
package info.nightscout.pump.medtrum
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import android.text.format.DateFormat
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.PreferenceFragmentCompat
import dagger.android.HasAndroidInjector
import info.nightscout.core.ui.toast.ToastUtils
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.interfaces.constraints.Constraint
import info.nightscout.interfaces.constraints.Constraints
import info.nightscout.interfaces.notifications.Notification
import info.nightscout.interfaces.plugin.PluginDescription
import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.profile.Profile
import info.nightscout.interfaces.pump.DetailedBolusInfo
import info.nightscout.interfaces.pump.Medtrum
import info.nightscout.interfaces.pump.Pump
import info.nightscout.interfaces.pump.PumpEnactResult
import info.nightscout.interfaces.pump.PumpPluginBase
import info.nightscout.interfaces.pump.PumpSync
import info.nightscout.interfaces.pump.TemporaryBasalStorage
import info.nightscout.interfaces.pump.actions.CustomAction
import info.nightscout.interfaces.pump.actions.CustomActionType
import info.nightscout.interfaces.pump.defs.ManufacturerType
import info.nightscout.interfaces.pump.defs.PumpDescription
import info.nightscout.interfaces.pump.defs.PumpType
import info.nightscout.interfaces.queue.Callback
import info.nightscout.interfaces.queue.CommandQueue
import info.nightscout.interfaces.queue.CustomCommand
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.utils.DecimalFormatter
import info.nightscout.interfaces.utils.TimeChangeType
import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState
import info.nightscout.pump.medtrum.ui.MedtrumOverviewFragment
import info.nightscout.pump.medtrum.services.MedtrumService
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventAppExit
import info.nightscout.rx.events.EventDismissNotification
import info.nightscout.rx.events.EventOverviewBolusProgress
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import org.json.JSONException
import org.json.JSONObject
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.abs
@Singleton class MedtrumPlugin @Inject constructor(
injector: HasAndroidInjector,
aapsLogger: AAPSLogger,
rh: ResourceHelper,
commandQueue: CommandQueue,
private val constraintChecker: Constraints,
private val aapsSchedulers: AapsSchedulers,
private val rxBus: RxBus,
private val context: Context,
private val fabricPrivacy: FabricPrivacy,
private val dateUtil: DateUtil,
private val medtrumPump: MedtrumPump,
private val uiInteraction: UiInteraction,
private val pumpSync: PumpSync,
private val temporaryBasalStorage: TemporaryBasalStorage
) : PumpPluginBase(
PluginDescription()
.mainType(PluginType.PUMP)
.fragmentClass(MedtrumOverviewFragment::class.java.name)
.pluginIcon(info.nightscout.core.ui.R.drawable.ic_medtrum_128)
.pluginName(R.string.medtrum)
.shortName(R.string.medtrum_pump_shortname)
.preferencesId(R.xml.pref_medtrum_pump)
.description(R.string.medtrum_pump_description), injector, aapsLogger, rh, commandQueue
), Pump, Medtrum {
private val disposable = CompositeDisposable()
private var medtrumService: MedtrumService? = null
override fun onStart() {
super.onStart()
aapsLogger.debug(LTag.PUMP, "MedtrumPlugin onStart()")
val intent = Intent(context, MedtrumService::class.java)
context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
disposable += rxBus
.toObservable(EventAppExit::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ context.unbindService(mConnection) }, fabricPrivacy::logException)
}
override fun onStop() {
aapsLogger.debug(LTag.PUMP, "MedtrumPlugin onStop()")
context.unbindService(mConnection)
disposable.clear()
super.onStop()
}
private val mConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName) {
aapsLogger.debug(LTag.PUMP, "Service is disconnected")
medtrumService = null
}
override fun onServiceConnected(name: ComponentName, service: IBinder) {
aapsLogger.debug(LTag.PUMP, "Service is connected")
val mLocalBinder = service as MedtrumService.LocalBinder
medtrumService = mLocalBinder.serviceInstance
}
}
fun getService(): MedtrumService? {
return medtrumService
}
override fun preprocessPreferences(preferenceFragment: PreferenceFragmentCompat) {
super.preprocessPreferences(preferenceFragment)
preferenceFragment.findPreference<EditTextPreference>(rh.gs(R.string.key_sn_input))?.isEnabled = !isInitialized()
val alarmSetting = preferenceFragment.findPreference<ListPreference>(rh.gs(R.string.key_alarm_setting))
val allAlarmEntries = preferenceFragment.resources.getStringArray(R.array.alarmSettings)
val allAlarmValues = preferenceFragment.resources.getStringArray(R.array.alarmSettingsValues)
if (allAlarmEntries.size < 8 || allAlarmValues.size < 8) {
aapsLogger.error(LTag.PUMP, "Alarm settings array is not complete")
return
}
when (medtrumPump.pumpType()) {
PumpType.MEDTRUM_NANO -> {
alarmSetting?.entries = arrayOf(allAlarmEntries[6], allAlarmEntries[7]) // "Beep", "Silent"
alarmSetting?.entryValues = arrayOf(allAlarmValues[6], allAlarmValues[7]) // "6", "7"
}
else -> {
// Use Nano settings for unknown pumps
alarmSetting?.entries = arrayOf(allAlarmEntries[6], allAlarmEntries[7]) // "Beep", "Silent"
alarmSetting?.entryValues = arrayOf(allAlarmValues[6], allAlarmValues[7]) // "6", "7"
}
}
}
override fun isInitialized(): Boolean {
return medtrumPump.pumpState > MedtrumPumpState.EJECTED && medtrumPump.pumpState < MedtrumPumpState.STOPPED
}
override fun isSuspended(): Boolean {
return medtrumPump.pumpState < MedtrumPumpState.ACTIVE || medtrumPump.pumpState > MedtrumPumpState.ACTIVE_ALT
}
override fun isBusy(): Boolean {
return false
}
override fun isConnected(): Boolean {
// This is a workaround to prevent AAPS to trigger connects when we have no patch activated
return if (!isInitialized()) true else medtrumService?.isConnected ?: false
}
override fun isConnecting(): Boolean = medtrumService?.isConnecting ?: false
override fun isHandshakeInProgress(): Boolean = false
override fun finishHandshaking() {
}
override fun connect(reason: String) {
if (isInitialized()) {
aapsLogger.debug(LTag.PUMP, "Medtrum connect - reason:$reason")
if (medtrumService != null) {
aapsLogger.debug(LTag.PUMP, "Medtrum connect - Attempt connection!")
val success = medtrumService?.connect(reason) ?: false
if (!success) ToastUtils.errorToast(context, info.nightscout.core.ui.R.string.ble_not_supported_or_not_paired)
}
}
}
override fun disconnect(reason: String) {
if (isInitialized()) {
aapsLogger.debug(LTag.PUMP, "Medtrum disconnect from: $reason")
medtrumService?.disconnect(reason)
}
}
override fun stopConnecting() {
if (isInitialized()) {
aapsLogger.debug(LTag.PUMP, "Medtrum stopConnecting")
medtrumService?.stopConnecting()
}
}
override fun getPumpStatus(reason: String) {
aapsLogger.debug(LTag.PUMP, "Medtrum getPumpStatus - reason:$reason")
if (isInitialized()) {
val connectionOK = medtrumService?.readPumpStatus() ?: false
if (connectionOK == false) {
aapsLogger.error(LTag.PUMP, "Medtrum getPumpStatus failed")
}
}
}
override fun setNewBasalProfile(profile: Profile): PumpEnactResult {
// New profile will be set when patch is activated
if (!isInitialized()) return PumpEnactResult(injector).success(true).enacted(true)
return if (medtrumService?.updateBasalsInPump(profile) == true) {
rxBus.send(EventDismissNotification(Notification.FAILED_UPDATE_PROFILE))
uiInteraction.addNotificationValidFor(Notification.PROFILE_SET_OK, rh.gs(info.nightscout.core.ui.R.string.profile_set_ok), Notification.INFO, 60)
PumpEnactResult(injector).success(true).enacted(true)
} else {
uiInteraction.addNotification(Notification.FAILED_UPDATE_PROFILE, rh.gs(info.nightscout.core.ui.R.string.failed_update_basal_profile), Notification.URGENT)
PumpEnactResult(injector)
}
}
override fun isThisProfileSet(profile: Profile): Boolean {
if (!isInitialized()) return true
var result = false
val profileBytes = medtrumPump.buildMedtrumProfileArray(profile)
if (profileBytes?.size == medtrumPump.actualBasalProfile.size) {
result = true
for (i in profileBytes.indices) {
if (profileBytes[i] != medtrumPump.actualBasalProfile[i]) {
result = false
break
}
}
}
return result
}
override fun lastDataTime(): Long = medtrumPump.lastConnection
override val baseBasalRate: Double
get() = medtrumPump.baseBasalRate
override val reservoirLevel: Double
get() = medtrumPump.reservoir
override val batteryLevel: Int
get() = 0 // We cannot determine battery level (yet)
@Synchronized
override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult {
aapsLogger.debug(LTag.PUMP, "deliverTreatment: " + detailedBolusInfo.insulin + "U")
if (!isInitialized()) return PumpEnactResult(injector).success(false).enacted(false)
detailedBolusInfo.insulin = constraintChecker.applyBolusConstraints(Constraint(detailedBolusInfo.insulin)).value()
return if (detailedBolusInfo.insulin > 0 && detailedBolusInfo.carbs == 0.0) {
aapsLogger.debug(LTag.PUMP, "deliverTreatment: Delivering bolus: " + detailedBolusInfo.insulin + "U")
val t = EventOverviewBolusProgress.Treatment(0.0, 0, detailedBolusInfo.bolusType == DetailedBolusInfo.BolusType.SMB, detailedBolusInfo.id)
val connectionOK = medtrumService?.setBolus(detailedBolusInfo, t) ?: false
val result = PumpEnactResult(injector)
result.success = connectionOK && abs(detailedBolusInfo.insulin - t.insulin) < pumpDescription.bolusStep
result.bolusDelivered = t.insulin
if (!result.success) {
// Note: There are no error codes
result.comment = "failed"
} else {
result.comment = "ok"
}
aapsLogger.debug(LTag.PUMP, "deliverTreatment: OK. Success: ${result.success} Asked: ${detailedBolusInfo.insulin} Delivered: ${result.bolusDelivered}")
result
} else {
aapsLogger.debug(LTag.PUMP, "deliverTreatment: Invalid input")
val result = PumpEnactResult(injector)
result.success = false
result.bolusDelivered = 0.0
result.comment = rh.gs(info.nightscout.core.ui.R.string.invalid_input)
aapsLogger.error("deliverTreatment: Invalid input")
result
}
}
override fun stopBolusDelivering() {
if (!isInitialized()) return
aapsLogger.info(LTag.PUMP, "stopBolusDelivering")
medtrumService?.stopBolus()
}
@Synchronized
override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult {
if (!isInitialized()) return PumpEnactResult(injector).success(false).enacted(false)
aapsLogger.info(LTag.PUMP, "setTempBasalAbsolute - absoluteRate: $absoluteRate, durationInMinutes: $durationInMinutes, enforceNew: $enforceNew")
// round rate to pump rate
val pumpRate = constraintChecker.applyBasalConstraints(Constraint(absoluteRate), profile).value()
temporaryBasalStorage.add(PumpSync.PumpState.TemporaryBasal(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), pumpRate, true, tbrType, 0L, 0L))
val connectionOK = medtrumService?.setTempBasal(pumpRate, durationInMinutes) ?: false
if (connectionOK
&& medtrumPump.tempBasalInProgress
&& Math.abs(medtrumPump.tempBasalAbsoluteRate - pumpRate) <= 0.05
) {
return PumpEnactResult(injector).success(true).enacted(true).duration(durationInMinutes).absolute(medtrumPump.tempBasalAbsoluteRate)
.isPercent(false)
.isTempCancel(false)
} else {
aapsLogger.error(
LTag.PUMP,
"setTempBasalAbsolute failed, connectionOK: $connectionOK, tempBasalInProgress: ${medtrumPump.tempBasalInProgress}, tempBasalAbsoluteRate: ${medtrumPump.tempBasalAbsoluteRate}"
)
return PumpEnactResult(injector).success(false).enacted(false).comment("Medtrum setTempBasalAbsolute failed")
}
}
override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult {
aapsLogger.info(LTag.PUMP, "setTempBasalPercent - percent: $percent, durationInMinutes: $durationInMinutes, enforceNew: $enforceNew")
return PumpEnactResult(injector).success(false).enacted(false).comment("Medtrum driver does not support percentage temp basals")
}
override fun setExtendedBolus(insulin: Double, durationInMinutes: Int): PumpEnactResult {
aapsLogger.info(LTag.PUMP, "setExtendedBolus - insulin: $insulin, durationInMinutes: $durationInMinutes")
return PumpEnactResult(injector).success(false).enacted(false).comment("Medtrum driver does not support extended boluses")
}
override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult {
if (!isInitialized()) return PumpEnactResult(injector).success(false).enacted(false)
aapsLogger.info(LTag.PUMP, "cancelTempBasal - enforceNew: $enforceNew")
val connectionOK = medtrumService?.cancelTempBasal() ?: false
if (connectionOK && !medtrumPump.tempBasalInProgress) {
return PumpEnactResult(injector).success(true).enacted(true).isTempCancel(true)
} else {
aapsLogger.error(LTag.PUMP, "cancelTempBasal failed, connectionOK: $connectionOK, tempBasalInProgress: ${medtrumPump.tempBasalInProgress}")
return PumpEnactResult(injector).success(false).enacted(false).comment("Medtrum cancelTempBasal failed")
}
}
override fun cancelExtendedBolus(): PumpEnactResult {
return PumpEnactResult(injector)
}
override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject {
val now = System.currentTimeMillis()
if (medtrumPump.lastConnection + 60 * 60 * 1000L < System.currentTimeMillis()) {
return JSONObject()
}
val pumpJson = JSONObject()
val status = JSONObject()
val extended = JSONObject()
try {
status.put(
"status", if (!isSuspended()) "normal"
else if (isInitialized() && isSuspended()) "suspended"
else "no active patch"
)
status.put("timestamp", dateUtil.toISOString(medtrumPump.lastConnection))
if (medtrumPump.lastBolusTime != 0L) {
extended.put("lastBolus", dateUtil.dateAndTimeString(medtrumPump.lastBolusTime))
extended.put("lastBolusAmount", medtrumPump.lastBolusAmount)
}
val tb = pumpSync.expectedPumpState().temporaryBasal
if (tb != null) {
extended.put("TempBasalAbsoluteRate", tb.convertedToAbsolute(now, profile))
extended.put("TempBasalStart", dateUtil.dateAndTimeString(tb.timestamp))
extended.put("TempBasalRemaining", tb.plannedRemainingMinutes)
}
extended.put("BaseBasalRate", baseBasalRate)
try {
extended.put("ActiveProfile", profileName)
} catch (ignored: Exception) {
}
pumpJson.put("status", status)
pumpJson.put("extended", extended)
pumpJson.put("reservoir", medtrumPump.reservoir.toInt())
pumpJson.put("clock", dateUtil.toISOString(now))
} catch (e: JSONException) {
aapsLogger.error(LTag.PUMP, "Unhandled exception: $e")
}
return pumpJson
}
override fun manufacturer(): ManufacturerType {
return ManufacturerType.Medtrum
}
override fun model(): PumpType {
return medtrumPump.pumpType()
}
override fun serialNumber(): String {
// Load from SP here, because this value will be get before pump is initialized
return medtrumPump.pumpSNFromSP.toString(radix = 16)
}
override val pumpDescription: PumpDescription
get() = PumpDescription(medtrumPump.pumpType())
override fun shortStatus(veryShort: Boolean): String {
var ret = ""
if (medtrumPump.lastConnection != 0L) {
val agoMillis = System.currentTimeMillis() - medtrumPump.lastConnection
val agoMin = (agoMillis / 60.0 / 1000.0).toInt()
ret += "LastConn: $agoMin minAgo\n"
}
if (medtrumPump.lastBolusTime != 0L)
ret += "LastBolus: ${DecimalFormatter.to2Decimal(medtrumPump.lastBolusAmount)}U @${DateFormat.format("HH:mm", medtrumPump.lastBolusTime)}\n"
if (medtrumPump.tempBasalInProgress)
ret += "Temp: ${medtrumPump.temporaryBasalToString()}\n"
ret += "Res: ${DecimalFormatter.to0Decimal(medtrumPump.reservoir)}U\n"
return ret
}
override val isFakingTempsByExtendedBoluses: Boolean = false
override fun loadTDDs(): PumpEnactResult {
return PumpEnactResult(injector) // Note: Can implement this if we implement history fully (no priority)
}
override fun getCustomActions(): List<CustomAction>? {
return null
}
override fun executeCustomAction(customActionType: CustomActionType) {
}
override fun executeCustomCommand(customCommand: CustomCommand): PumpEnactResult? {
return null
}
override fun canHandleDST(): Boolean {
return true
}
override fun timezoneOrDSTChanged(timeChangeType: TimeChangeType) {
medtrumPump.needCheckTimeUpdate = true
if (isInitialized()) {
commandQueue.updateTime(object : Callback() {
override fun run() {
if (this.result.success == false) {
aapsLogger.error(LTag.PUMP, "Medtrum time update failed")
// Only notify here on failure (connection may be failed), service will handle success
medtrumService?.timeUpdateNotification(false)
}
}
})
}
}
// Medtrum interface
override fun loadEvents(): PumpEnactResult {
if (!isInitialized()) {
val result = PumpEnactResult(injector).success(false)
result.comment = "pump not initialized"
return result
}
val connectionOK = medtrumService?.loadEvents() ?: false
return PumpEnactResult(injector).success(connectionOK)
}
override fun setUserOptions(): PumpEnactResult {
if (!isInitialized()) {
val result = PumpEnactResult(injector).success(false)
result.comment = "pump not initialized"
return result
}
val connectionOK = medtrumService?.setUserSettings() ?: false
return PumpEnactResult(injector).success(connectionOK)
}
override fun clearAlarms(): PumpEnactResult {
if (!isInitialized()) {
val result = PumpEnactResult(injector).success(false)
result.comment = "pump not initialized"
return result
}
val connectionOK = medtrumService?.clearAlarms() ?: false
return PumpEnactResult(injector).success(connectionOK)
}
override fun deactivate(): PumpEnactResult {
val connectionOK = medtrumService?.deactivatePatch() ?: false
return PumpEnactResult(injector).success(connectionOK)
}
override fun updateTime(): PumpEnactResult {
if (!isInitialized()) {
val result = PumpEnactResult(injector).success(false)
result.comment = "pump not initialized"
return result
}
val connectionOK = medtrumService?.updateTimeIfNeeded() ?: false
return PumpEnactResult(injector).success(connectionOK)
}
}

View file

@ -0,0 +1,539 @@
package info.nightscout.pump.medtrum
import android.util.Base64
import info.nightscout.interfaces.profile.Profile
import info.nightscout.interfaces.pump.PumpSync
import info.nightscout.interfaces.pump.TemporaryBasalStorage
import info.nightscout.interfaces.pump.defs.PumpType
import info.nightscout.pump.medtrum.code.ConnectionState
import info.nightscout.pump.medtrum.comm.enums.AlarmSetting
import info.nightscout.pump.medtrum.comm.enums.AlarmState
import info.nightscout.pump.medtrum.comm.enums.BasalType
import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.rx.events.EventOverviewBolusProgress
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.round
@Singleton
class MedtrumPump @Inject constructor(
private val aapsLogger: AAPSLogger,
private val rh: ResourceHelper,
private val sp: SP,
private val dateUtil: DateUtil,
private val pumpSync: PumpSync,
private val temporaryBasalStorage: TemporaryBasalStorage
) {
companion object {
const val FAKE_TBR_LENGTH = 4800L
}
// Connection state flow
private val _connectionState = MutableStateFlow(ConnectionState.DISCONNECTED)
val connectionStateFlow: StateFlow<ConnectionState> = _connectionState
var connectionState: ConnectionState
get() = _connectionState.value
set(value) {
_connectionState.value = value
}
// Pump state flow
private val _pumpState = MutableStateFlow(MedtrumPumpState.NONE)
val pumpStateFlow: StateFlow<MedtrumPumpState> = _pumpState
var pumpState: MedtrumPumpState
get() = _pumpState.value
set(value) {
_pumpState.value = value
sp.putInt(R.string.key_pump_state, value.state.toInt())
}
// Active alarms
private var _activeAlarms: EnumSet<AlarmState> = EnumSet.noneOf(AlarmState::class.java)
var activeAlarms: EnumSet<AlarmState>
get() = _activeAlarms
set(value) {
_activeAlarms = value
}
// Prime progress as state flow
private val _primeProgress = MutableStateFlow(0)
val primeProgressFlow: StateFlow<Int> = _primeProgress
var primeProgress: Int
get() = _primeProgress.value
set(value) {
_primeProgress.value = value
}
private var _lastBasalType: MutableStateFlow<BasalType> = MutableStateFlow(BasalType.NONE)
val lastBasalTypeFlow: StateFlow<BasalType> = _lastBasalType
val lastBasalType: BasalType
get() = _lastBasalType.value
private val _lastBasalRate = MutableStateFlow(0.0)
val lastBasalRateFlow: StateFlow<Double> = _lastBasalRate
val lastBasalRate: Double
get() = _lastBasalRate.value
private val _reservoir = MutableStateFlow(0.0)
val reservoirFlow: StateFlow<Double> = _reservoir
var reservoir: Double
get() = _reservoir.value
set(value) {
_reservoir.value = value
}
var batteryVoltage_A = 0.0 // Not used in UI
private val _batteryVoltage_B = MutableStateFlow(0.0)
val batteryVoltage_BFlow: StateFlow<Double> = _batteryVoltage_B
var batteryVoltage_B: Double
get() = _batteryVoltage_B.value
set(value) {
_batteryVoltage_B.value = value
}
/** Stuff stored in SP */
private var _patchSessionToken = 0L
var patchSessionToken: Long
get() = _patchSessionToken
set(value) {
_patchSessionToken = value
sp.putLong(R.string.key_session_token, value)
}
private var _patchId = 0L
var patchId: Long
get() = _patchId
set(value) {
_patchId = value
sp.putLong(R.string.key_patch_id, value)
}
private var _currentSequenceNumber = 0
var currentSequenceNumber: Int
get() = _currentSequenceNumber
set(value) {
_currentSequenceNumber = value
sp.putInt(R.string.key_current_sequence_number, value)
}
private var _syncedSequenceNumber = 0
var syncedSequenceNumber: Int
get() = _syncedSequenceNumber
set(value) {
_syncedSequenceNumber = value
sp.putInt(R.string.key_synced_sequence_number, value)
}
private var _actualBasalProfile = byteArrayOf(0)
var actualBasalProfile: ByteArray
get() = _actualBasalProfile
set(value) {
_actualBasalProfile = value
val encodedString = Base64.encodeToString(value, Base64.DEFAULT)
sp.putString(R.string.key_actual_basal_profile, encodedString ?: "")
}
private var _lastBolusTime = 0L // Time in ms!
var lastBolusTime: Long
get() = _lastBolusTime
set(value) {
_lastBolusTime = value
sp.putLong(R.string.key_last_bolus_time, value)
}
private var _lastBolusAmount = 0.0
var lastBolusAmount: Double
get() = _lastBolusAmount
set(value) {
_lastBolusAmount = value
sp.putDouble(R.string.key_last_bolus_amount, value)
}
private var _lastConnection = 0L // Time in ms!
var lastConnection: Long
get() = _lastConnection
set(value) {
_lastConnection = value
sp.putLong(R.string.key_last_connection, value)
}
private var _deviceType: Int = 80 // As reported by pump
var deviceType: Int
get() = _deviceType
set(value) {
_deviceType = value
sp.putInt(R.string.key_device_type, value)
}
private var _swVersion: String = "" // As reported by pump
var swVersion: String
get() = _swVersion
set(value) {
_swVersion = value
sp.putString(R.string.key_sw_version, value)
}
private var _patchStartTime = 0L // Time in ms!
var patchStartTime: Long
get() = _patchStartTime
set(value) {
_patchStartTime = value
sp.putLong(R.string.key_patch_start_time, value)
}
private var _pumpTimeZoneOffset = 0 // As reported by pump
var pumpTimeZoneOffset: Int
get() = _pumpTimeZoneOffset
set(value) {
_pumpTimeZoneOffset = value
sp.putInt(R.string.key_pump_time_zone_offset, value)
}
private var _pumpSN = 0L
val pumpSN: Long
get() = _pumpSN
val pumpSNFromSP: Long
get() =
try {
sp.getString(R.string.key_sn_input, "0").toLong(radix = 16)
} catch (e: NumberFormatException) {
aapsLogger.debug(LTag.PUMP, "pumpSNFromSP: Invalid input!")
0L
}
var needCheckTimeUpdate = false
var lastTimeReceivedFromPump = 0L // Time in ms!
var suspendTime = 0L // Time in ms!
var patchAge = 0L // Time in seconds?! // As reported by pump, not used (yet)
// bolus status
var bolusingTreatment: EventOverviewBolusProgress.Treatment? = null // actually delivered treatment
var bolusAmountToBeDelivered = 0.0 // amount to be delivered
var bolusProgressLastTimeStamp: Long = 0 // timestamp of last bolus progress message
var bolusStopped = false // bolus stopped by user
var bolusDone = false // Bolus completed or stopped on pump
// Last basal status update (from pump)
private var _lastBasalSequence = 0
val lastBasalSequence: Int
get() = _lastBasalSequence
private var _lastBasalPatchId = 0L
val lastBasalPatchId: Long
get() = _lastBasalPatchId
private var _lastBasalStartTime = 0L
val lastBasalStartTime: Long
get() = _lastBasalStartTime
val baseBasalRate: Double
get() = getHourlyBasalFromMedtrumProfileArray(actualBasalProfile, dateUtil.now())
// TBR status
val tempBasalInProgress: Boolean
get() = lastBasalType == BasalType.ABSOLUTE_TEMP || lastBasalType == BasalType.RELATIVE_TEMP
val tempBasalAbsoluteRate: Double
get() = if (tempBasalInProgress) lastBasalRate else 0.0
// Last stop status update
var lastStopSequence = 0
var lastStopPatchId = 0L
// User settings (desired values, to be set on pump)
var desiredPatchExpiration = false
var desiredAlarmSetting = AlarmSetting.LIGHT_VIBRATE_AND_BEEP
var desiredHourlyMaxInsulin: Int = 40
var desiredDailyMaxInsulin: Int = 180
init {
// Load stuff from SP
_patchSessionToken = sp.getLong(R.string.key_session_token, 0L)
_lastConnection = sp.getLong(R.string.key_last_connection, 0L)
_lastBolusTime = sp.getLong(R.string.key_last_bolus_time, 0L)
_lastBolusAmount = sp.getDouble(R.string.key_last_bolus_amount, 0.0)
_currentSequenceNumber = sp.getInt(R.string.key_current_sequence_number, 0)
_patchId = sp.getLong(R.string.key_patch_id, 0L)
_syncedSequenceNumber = sp.getInt(R.string.key_synced_sequence_number, 0)
_pumpState.value = MedtrumPumpState.fromByte(sp.getInt(R.string.key_pump_state, MedtrumPumpState.NONE.state.toInt()).toByte())
_deviceType = sp.getInt(R.string.key_device_type, 0)
_swVersion = sp.getString(R.string.key_sw_version, "")
_patchStartTime = sp.getLong(R.string.key_patch_start_time, 0L)
_pumpTimeZoneOffset = sp.getInt(R.string.key_pump_time_zone_offset, 0)
loadActiveAlarms()
val encodedString = sp.getString(R.string.key_actual_basal_profile, "0")
try {
_actualBasalProfile = Base64.decode(encodedString, Base64.DEFAULT)
} catch (e: Exception) {
aapsLogger.error(LTag.PUMP, "Error decoding basal profile from SP: $encodedString")
}
}
fun pumpType(): PumpType =
when (deviceType) {
80, 88 -> PumpType.MEDTRUM_NANO
else -> PumpType.MEDTRUM_UNTESTED
}
fun loadUserSettingsFromSP() {
desiredPatchExpiration = sp.getBoolean(info.nightscout.pump.medtrum.R.string.key_patch_expiration, false)
val alarmSettingCode = sp.getString(info.nightscout.pump.medtrum.R.string.key_alarm_setting, AlarmSetting.LIGHT_VIBRATE_AND_BEEP.code.toString()).toByte()
desiredAlarmSetting = AlarmSetting.values().firstOrNull { it.code == alarmSettingCode } ?: AlarmSetting.LIGHT_VIBRATE_AND_BEEP
desiredHourlyMaxInsulin = sp.getInt(info.nightscout.pump.medtrum.R.string.key_hourly_max_insulin, 40)
desiredDailyMaxInsulin = sp.getInt(info.nightscout.pump.medtrum.R.string.key_daily_max_insulin, 180)
_pumpSN = pumpSNFromSP
}
fun buildMedtrumProfileArray(nsProfile: Profile): ByteArray? {
val list = nsProfile.getBasalValues()
var basals = byteArrayOf()
for (item in list) {
val rate = round(item.value / 0.05).toInt()
val time = item.timeAsSeconds / 60
if (rate > 0xFFF || time > 0xFFF) {
aapsLogger.error(LTag.PUMP, "buildMedtrumProfileArray: rate or time too large: $rate, $time")
return null
}
basals += ((rate shl 12) + time).toByteArray(3)
aapsLogger.debug(LTag.PUMP, "buildMedtrumProfileArray: value: ${item.value} time: ${item.timeAsSeconds}, converted: $rate, $time")
}
return (list.size).toByteArray(1) + basals
}
fun getHourlyBasalFromMedtrumProfileArray(basalProfile: ByteArray, timestamp: Long): Double {
val basalCount = basalProfile[0].toInt()
var basal = 0.0
if (basalProfile.size < 4 || (basalProfile.size - 1) % 3 != 0 || basalCount > 24) {
aapsLogger.debug(LTag.PUMP, "getHourlyBasalFromMedtrumProfileArray: No valid basal profile set")
return basal
}
val date = GregorianCalendar()
date.timeInMillis = timestamp
val hourOfDayMinutes = date.get(GregorianCalendar.HOUR_OF_DAY) * 60 + date.get(GregorianCalendar.MINUTE)
for (index in 0 until basalCount) {
val currentIndex = 1 + (index * 3)
val nextIndex = currentIndex + 3
val rateAndTime = basalProfile.copyOfRange(currentIndex, nextIndex).toInt()
val rate = (rateAndTime shr 12) * 0.05
val startMinutes = rateAndTime and 0xFFF
val endMinutes = if (nextIndex < basalProfile.size) {
val nextRateAndTime = basalProfile.copyOfRange(nextIndex, nextIndex + 3).toInt()
nextRateAndTime and 0xFFF
} else {
24 * 60
}
if (hourOfDayMinutes in startMinutes until endMinutes) {
basal = rate
aapsLogger.debug(LTag.PUMP, "getHourlyBasalFromMedtrumProfileArray: basal: $basal")
break
}
// aapsLogger.debug(LTag.PUMP, "getHourlyBasalFromMedtrumProfileArray: rate: $rate, startMinutes: $startMinutes, endMinutes: $endMinutes")
}
return basal
}
fun handleBolusStatusUpdate(bolusType: Int, bolusCompleted: Boolean, amountDelivered: Double) {
aapsLogger.debug(LTag.PUMP, "handleBolusStatusUpdate: bolusType: $bolusType bolusCompleted: $bolusCompleted amountDelivered: $amountDelivered")
bolusProgressLastTimeStamp = dateUtil.now()
bolusingTreatment?.insulin = amountDelivered
bolusDone = bolusCompleted
}
fun handleBasalStatusUpdate(basalType: BasalType, basalValue: Double, basalSequence: Int, basalPatchId: Long, basalStartTime: Long) {
handleBasalStatusUpdate(basalType, basalValue, basalSequence, basalPatchId, basalStartTime, dateUtil.now())
}
fun handleBasalStatusUpdate(basalType: BasalType, basalRate: Double, basalSequence: Int, basalPatchId: Long, basalStartTime: Long, receivedTime: Long) {
aapsLogger.debug(
LTag.PUMP,
"handleBasalStatusUpdate: basalType: $basalType basalValue: $basalRate basalSequence: $basalSequence basalPatchId: $basalPatchId basalStartTime: $basalStartTime " + "receivedTime: $receivedTime"
)
@Suppress("UNNECESSARY_SAFE_CALL") // Safe call to allow mocks to return null
val expectedTemporaryBasal = pumpSync.expectedPumpState()?.temporaryBasal
if (basalType.isTempBasal() && expectedTemporaryBasal?.pumpId != basalStartTime) {
// Note: temporaryBasalInfo will be removed from temporaryBasalStorage after this call
val temporaryBasalInfo = temporaryBasalStorage.findTemporaryBasal(basalStartTime, basalRate)
// If duration is unknown, no way to get it now, set patch lifetime as duration
val duration = temporaryBasalInfo?.duration ?: T.mins(FAKE_TBR_LENGTH).msecs()
val adjustedBasalRate = if (basalType == BasalType.ABSOLUTE_TEMP) {
basalRate
} else {
(basalRate / baseBasalRate) * 100 // calculate the percentage of the original basal rate
}
val newRecord = pumpSync.syncTemporaryBasalWithPumpId(
timestamp = basalStartTime,
rate = adjustedBasalRate,
duration = duration,
isAbsolute = (basalType == BasalType.ABSOLUTE_TEMP),
type = temporaryBasalInfo?.type,
pumpId = basalStartTime,
pumpType = pumpType(),
pumpSerial = pumpSN.toString(radix = 16)
)
aapsLogger.debug(
LTag.PUMPCOMM,
"handleBasalStatusUpdate: ${if (newRecord) "**NEW** " else ""}EVENT TEMP_START ($basalType) ${dateUtil.dateAndTimeString(basalStartTime)} ($basalStartTime) " + "Rate: $basalRate Duration: ${duration} temporaryBasalInfo: $temporaryBasalInfo, expectedTemporaryBasal: $expectedTemporaryBasal"
)
} else if (basalType.isSuspendedByPump() && expectedTemporaryBasal?.pumpId != basalStartTime) {
val newRecord = pumpSync.syncTemporaryBasalWithPumpId(
timestamp = basalStartTime,
rate = 0.0,
duration = T.mins(FAKE_TBR_LENGTH).msecs(),
isAbsolute = true,
type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
pumpId = basalStartTime,
pumpType = pumpType(),
pumpSerial = pumpSN.toString(radix = 16)
)
aapsLogger.debug(
LTag.PUMPCOMM,
"handleBasalStatusUpdate: ${if (newRecord) "**NEW** " else ""}EVENT TEMP_START ($basalType) ${dateUtil.dateAndTimeString(basalStartTime)} ($basalStartTime) expectedTemporaryBasal: $expectedTemporaryBasal"
)
} else if (basalType == BasalType.NONE && expectedTemporaryBasal?.rate != basalRate && expectedTemporaryBasal?.duration != T.mins(FAKE_TBR_LENGTH).msecs()) {
// Pump suspended, set fake TBR
setFakeTBR()
} else if (basalType == BasalType.STANDARD) {
if (expectedTemporaryBasal != null) {
// Pump resumed, sync end
val success = pumpSync.syncStopTemporaryBasalWithPumpId(
timestamp = basalStartTime + 250, // Time of normal basal start = time of tbr end
endPumpId = basalStartTime + 250, // +250ms Make sure there is time between start and stop of TBR
pumpType = pumpType(),
pumpSerial = pumpSN.toString(radix = 16)
)
aapsLogger.debug(LTag.PUMPCOMM, "handleBasalStatusUpdate: EVENT TEMP_END ${dateUtil.dateAndTimeString(basalStartTime)} ($basalStartTime) success: $success")
}
}
// Update medtrum pump state
_lastBasalType.value = basalType
_lastBasalRate.value = basalRate
_lastBasalSequence = basalSequence
if (basalSequence > currentSequenceNumber) {
currentSequenceNumber = basalSequence
}
_lastBasalPatchId = basalPatchId
if (basalPatchId != patchId) {
aapsLogger.error(LTag.PUMP, "handleBasalStatusUpdate: WTF? PatchId in status update does not match current patchId!")
}
_lastBasalStartTime = basalStartTime
}
fun handleStopStatusUpdate(stopSequence: Int, stopPatchId: Long) {
aapsLogger.debug(LTag.PUMP, "handleStopStatusUpdate: stopSequence: $stopSequence stopPatchId: $stopPatchId")
lastStopSequence = stopSequence
if (stopSequence > currentSequenceNumber) {
currentSequenceNumber = stopSequence
}
lastStopPatchId = stopPatchId
if (stopPatchId != patchId) {
aapsLogger.error(LTag.PUMP, "handleStopStatusUpdate: WTF? PatchId in status update does not match current patchId!")
}
}
fun setFakeTBRIfNeeded() {
val expectedTemporaryBasal = pumpSync.expectedPumpState().temporaryBasal
if (expectedTemporaryBasal?.duration != T.mins(FAKE_TBR_LENGTH).msecs()) {
setFakeTBR()
}
}
private fun setFakeTBR() {
val newRecord = pumpSync.syncTemporaryBasalWithPumpId(
timestamp = dateUtil.now(),
rate = 0.0,
duration = T.mins(FAKE_TBR_LENGTH).msecs(),
isAbsolute = true,
type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
pumpId = dateUtil.now(),
pumpType = pumpType(),
pumpSerial = pumpSN.toString(radix = 16)
)
aapsLogger.debug(
LTag.PUMPCOMM,
"handleBasalStatusUpdate: ${if (newRecord) "**NEW** " else ""}EVENT TEMP_START (FAKE)"
)
}
fun temporaryBasalToString(): String {
if (!tempBasalInProgress) return ""
return tempBasalAbsoluteRate.toString() + "U/h"
}
fun addAlarm(alarm: AlarmState) {
activeAlarms.add(alarm)
saveActiveAlarms()
}
fun removeAlarm(alarm: AlarmState) {
activeAlarms.remove(alarm)
saveActiveAlarms()
}
fun clearAlarmState() {
activeAlarms.clear()
saveActiveAlarms()
}
fun alarmStateToString(alarmState: AlarmState): String {
val stringId = when (alarmState) {
AlarmState.NONE -> R.string.alarm_none
AlarmState.PUMP_LOW_BATTERY -> R.string.alarm_pump_low_battery
AlarmState.PUMP_LOW_RESERVOIR -> R.string.alarm_pump_low_reservoir
AlarmState.PUMP_EXPIRES_SOON -> R.string.alarm_pump_expires_soon
AlarmState.LOWBG_SUSPENDED -> R.string.alarm_lowbg_suspended
AlarmState.LOWBG_SUSPENDED2 -> R.string.alarm_lowbg_suspended2
AlarmState.AUTO_SUSPENDED -> R.string.alarm_auto_suspended
AlarmState.HMAX_SUSPENDED -> R.string.alarm_hmax_suspended
AlarmState.DMAX_SUSPENDED -> R.string.alarm_dmax_suspended
AlarmState.SUSPENDED -> R.string.alarm_suspended
AlarmState.PAUSED -> R.string.alarm_paused
AlarmState.OCCLUSION -> R.string.alarm_occlusion
AlarmState.EXPIRED -> R.string.alarm_expired
AlarmState.RESERVOIR_EMPTY -> R.string.alarm_reservoir_empty
AlarmState.PATCH_FAULT -> R.string.alarm_patch_fault
AlarmState.PATCH_FAULT2 -> R.string.alarm_patch_fault2
AlarmState.BASE_FAULT -> R.string.alarm_base_fault
AlarmState.BATTERY_OUT -> R.string.alarm_battery_out
AlarmState.NO_CALIBRATION -> R.string.alarm_no_calibration
}
return rh.gs(stringId)
}
private fun saveActiveAlarms() {
val alarmsStr = activeAlarms.joinToString(separator = ",") { it.name }
sp.putString(R.string.key_active_alarms, alarmsStr)
}
private fun loadActiveAlarms() {
val alarmsStr = sp.getString(R.string.key_active_alarms, "")
if (alarmsStr.isNullOrEmpty()) {
activeAlarms = EnumSet.noneOf(AlarmState::class.java)
} else {
activeAlarms = alarmsStr.split(",")
.mapNotNull { AlarmState.values().find { alarm -> alarm.name == it } }
.let { EnumSet.copyOf(it) }
}
}
}

View file

@ -0,0 +1,26 @@
package info.nightscout.pump.medtrum.bindingadapters
import android.view.View
import java.util.concurrent.atomic.AtomicBoolean
class OnSafeClickListener(
private val clickListener: View.OnClickListener,
private val intervalMs: Long = MIN_CLICK_INTERVAL
) : View.OnClickListener {
private var canClick = AtomicBoolean(true)
override fun onClick(v: View?) {
if (canClick.getAndSet(false)) {
v?.run {
postDelayed({
canClick.set(true)
}, intervalMs)
clickListener.onClick(v)
}
}
}
companion object {
// Set duplicate click prevention time
private const val MIN_CLICK_INTERVAL: Long = 1000
}
}

View file

@ -0,0 +1,29 @@
package info.nightscout.pump.medtrum.bindingadapters
import android.view.View
import android.widget.TextView
import androidx.annotation.ColorRes
import androidx.databinding.BindingAdapter
import info.nightscout.pump.medtrum.extension.setVisibleOrGone
@BindingAdapter("android:visibility")
fun setVisibility(view: View, visible: Boolean) {
view.setVisibleOrGone(visible)
}
@BindingAdapter("visibleOrGone")
fun setVisibleOrGone(view: View, visibleOrGone: Boolean) {
view.setVisibleOrGone(visibleOrGone)
}
@BindingAdapter("onSafeClick")
fun View.setOnSafeClickListener(clickListener: View.OnClickListener?) {
clickListener?.also {
setOnClickListener(OnSafeClickListener(it))
} ?: setOnClickListener(null)
}
@BindingAdapter("textColor")
fun setTextColor(view: TextView, @ColorRes colorResId: Int) {
view.setTextColor(view.context.getColor(colorResId))
}

View file

@ -0,0 +1,8 @@
package info.nightscout.pump.medtrum.code
enum class ConnectionState {
CONNECTED,
DISCONNECTED,
CONNECTING,
DISCONNECTING;
}

View file

@ -0,0 +1,7 @@
package info.nightscout.pump.medtrum.code
enum class EventType {
CHANGE_PATCH_CLICKED,
PROFILE_NOT_SET,
;
}

View file

@ -0,0 +1,21 @@
package info.nightscout.pump.medtrum.code
enum class PatchStep {
START_DEACTIVATION,
DEACTIVATE,
FORCE_DEACTIVATION,
DEACTIVATION_COMPLETE,
PREPARE_PATCH,
PREPARE_PATCH_CONNECT,
PRIME,
PRIMING,
PRIME_COMPLETE,
ATTACH_PATCH,
ACTIVATE,
ACTIVATE_COMPLETE,
RETRY_ACTIVATION,
RETRY_ACTIVATION_CONNECT,
ERROR,
CANCEL,
COMPLETE;
}

View file

@ -0,0 +1,36 @@
package info.nightscout.pump.medtrum.comm
import kotlin.experimental.and
import info.nightscout.pump.medtrum.extension.toLong
class ManufacturerData(private val manufacturerDataBytes: ByteArray) {
private var deviceID: Long = 0
private var deviceType = 0
private var version = 0
init {
setData(manufacturerDataBytes)
}
fun setData(inputData: ByteArray) {
var index = 0
val deviceIDBytes: ByteArray = inputData.copyOfRange(index, index + 4)
deviceID = deviceIDBytes.toLong()
index += 4
deviceType = (inputData[index] and 0xff.toByte()).toInt()
index += 1
version = (inputData[index] and 0xff.toByte()).toInt()
}
fun getDeviceSN(): Long{
return deviceID
}
fun getDeviceType(): Int {
return deviceType
}
fun getVersion(): Int {
return version
}
}

View file

@ -0,0 +1,19 @@
package info.nightscout.pump.medtrum.comm
class ReadDataPacket(data: ByteArray) {
private var totalData = data.copyOfRange(0, data.size - 1) // Strip crc
private var dataSize: Byte = data[0]
fun addData(newData: ByteArray) {
totalData += newData.copyOfRange(4, newData.size - 1) // Strip header and crc
}
fun allDataReceived(): Boolean {
return (totalData.size >= dataSize)
}
fun getData(): ByteArray {
return totalData
}
}

View file

@ -0,0 +1,65 @@
package info.nightscout.pump.medtrum.comm
class WriteCommandPackets(data: ByteArray, sequenceNumber: Int) {
private val CRC_8_TABLE: IntArray = intArrayOf(0, 155, 173, 54, 193, 90, 108, 247, 25, 130, 180, 47, 216, 67, 117, 238, 50, 169, 159, 4, 243, 104, 94, 197, 43, 176, 134, 29, 234, 113, 71, 220, 100, 255, 201, 82, 165, 62, 8, 147, 125, 230, 208, 75, 188, 39, 17, 138, 86, 205, 251, 96, 151, 12, 58, 161, 79, 212, 226, 121, 142, 21, 35, 184, 200, 83, 101, 254, 9, 146, 164, 63, 209, 74, 124, 231, 16, 139, 189, 38, 250, 97, 87, 204, 59, 160, 150, 13, 227, 120, 78, 213, 34, 185, 143, 20, 172, 55, 1, 154, 109, 246, 192, 91, 181, 46, 24, 131, 116, 239, 217, 66, 158, 5, 51, 168, 95, 196, 242, 105, 135, 28, 42, 177, 70, 221, 235, 112, 11, 144, 166, 61, 202, 81, 103, 252, 18, 137, 191, 36, 211, 72, 126, 229, 57, 162, 148, 15, 248, 99, 85, 206, 32, 187, 141, 22, 225, 122, 76, 215, 111, 244, 194, 89, 174, 53, 3, 152, 118, 237, 219, 64, 183, 44, 26, 129, 93, 198, 240, 107, 156, 7, 49, 170, 68, 223, 233, 114, 133, 30, 40, 179, 195, 88, 110, 245, 2, 153, 175, 52, 218, 65, 119, 236, 27, 128, 182, 45, 241, 106, 92, 199, 48, 171, 157, 6, 232, 115, 69, 222, 41, 178, 132, 31, 167, 60, 10, 145, 102, 253, 203, 80, 190, 37, 19, 136, 127, 228, 210, 73, 149, 14, 56, 163, 84, 207, 249, 98, 140, 23, 33, 186, 77, 214, 224, 123)
private val packages = mutableListOf<ByteArray>()
private var index = 0
init {
// PackageIndex: 0 initially, if there are multiple packets, for the first packet it is set to 0 (not included in CRC calculation but sent in actual header)
var pkgIndex = 0
var header = byteArrayOf(
(data.size + 4).toByte(),
data[0],
sequenceNumber.toByte(),
pkgIndex.toByte()
)
var tmp: ByteArray = header + data.copyOfRange(1, data.size)
var totalCommand: ByteArray = tmp + calcCrc8(tmp, tmp.size).toByte()
if ((totalCommand.size - header.size) <= 15) {
packages.add(totalCommand + 0.toByte())
} else {
pkgIndex = 1
var remainingCommand = totalCommand.copyOfRange(4, totalCommand.size)
while (remainingCommand.size > 15) {
header[3] = pkgIndex.toByte()
tmp = header + remainingCommand.copyOfRange(0, 15)
packages.add(tmp + calcCrc8(tmp, tmp.size).toByte())
remainingCommand = remainingCommand.copyOfRange(15, remainingCommand.size)
pkgIndex = (pkgIndex + 1) % 256
}
// Add last package
header[3] = pkgIndex.toByte()
tmp = header + remainingCommand
packages.add(tmp + calcCrc8(tmp, tmp.size).toByte())
}
}
fun getNextPacket(): ByteArray? {
var ret: ByteArray? = null
if (index < packages.size) {
ret = packages[index]
index++
}
return ret
}
fun allPacketsConsumed(): Boolean {
return !(index < packages.size)
}
private fun calcCrc8(value: ByteArray, size: Int): Int {
var crc8 = 0
for (i in 0 until size) {
crc8 = CRC_8_TABLE[(value[i].toInt() and 255) xor (crc8 and 255)].toInt() and 255
}
return crc8
}
}

View file

@ -0,0 +1,12 @@
package info.nightscout.pump.medtrum.comm.enums
enum class AlarmSetting(val code: Byte) {
LIGHT_VIBRATE_AND_BEEP(0),
LIGHT_AND_VIBRATE(1),
LIGHT_AND_BEEP(2),
LIGHT_ONLY(3),
VIBRATE_AND_BEEP(4),
VIBRATE_ONLY(5),
BEEP_ONLY(6),
NONE(7)
}

View file

@ -0,0 +1,23 @@
package info.nightscout.pump.medtrum.comm.enums
enum class AlarmState {
NONE,
PUMP_LOW_BATTERY, // Mapped from error flag 1
PUMP_LOW_RESERVOIR, // Mapped from error flag 2
PUMP_EXPIRES_SOON, // Mapped from error flag 3
LOWBG_SUSPENDED, // Mapped from pump status 64
LOWBG_SUSPENDED2, // Mapped from pump status 65
AUTO_SUSPENDED, // Mapped from pump status 66
HMAX_SUSPENDED, // Mapped from pump status 67
DMAX_SUSPENDED, // Mapped from pump status 68
SUSPENDED, // Mapped from pump status 69
PAUSED, // Mapped from pump status 70
OCCLUSION, // Mapped from pump status 96
EXPIRED, // Mapped from pump status 97
RESERVOIR_EMPTY, // Mapped from pump status 98
PATCH_FAULT, // Mapped from pump status 99
PATCH_FAULT2, // Mapped from pump status 100
BASE_FAULT, // Mapped from pump status 101
BATTERY_OUT, // Mapped from pump status 102
NO_CALIBRATION // Mapped from pump status 103
}

View file

@ -0,0 +1,54 @@
package info.nightscout.pump.medtrum.comm.enums
enum class BasalType {
NONE,
STANDARD,
EXERCISE,
HOLIDAY,
PROGRAM_A,
PROGRAM_B,
ABSOLUTE_TEMP,
RELATIVE_TEMP,
PROGRAM_C,
PROGRAM_D,
SICK,
AUTO,
NEW,
SUSPEND_LOW_GLUCOSE,
SUSPEND_PREDICT_LOW_GLUCOSE,
SUSPEND_AUTO,
SUSPEND_MORE_THAN_MAX_PER_HOUR,
SUSPEND_MORE_THAN_MAX_PER_DAY,
SUSPEND_MANUAL,
SUSPEND_KEY_LOST,
STOP_OCCLUSION,
STOP_EXPIRED,
STOP_EMPTY,
STOP_PATCH_FAULT,
STOP_PATCH_FAULT2,
STOP_BASE_FAULT,
STOP_DISCARD,
STOP_BATTERY_EXHAUSTED,
STOP,
PAUSE_INTERRUPT,
PRIME,
AUTO_MODE_START,
AUTO_MODE_EXIT,
AUTO_MODE_TARGET_100,
AUTO_MODE_TARGET_110,
AUTO_MODE_TARGET_120,
AUTO_MODE_BREAKFAST,
AUTO_MODE_LUNCH,
AUTO_MODE_DINNER,
AUTO_MODE_SNACK,
AUTO_MODE_EXERCISE_START,
AUTO_MODE_EXERCISE_EXIT;
fun isTempBasal(): Boolean {
return this == ABSOLUTE_TEMP || this == RELATIVE_TEMP
}
fun isSuspendedByPump(): Boolean {
return this in SUSPEND_LOW_GLUCOSE..STOP
}
}

View file

@ -0,0 +1,8 @@
package info.nightscout.pump.medtrum.comm.enums
enum class BolusType {
NONE,
NORMAL,
EXTENDED,
COMBI;
}

View file

@ -0,0 +1,26 @@
package info.nightscout.pump.medtrum.comm.enums
enum class CommandType(val code: Byte) {
SYNCHRONIZE(3),
SUBSCRIBE(4),
AUTH_REQ(5),
GET_DEVICE_TYPE(6),
SET_TIME(10),
GET_TIME(11),
SET_TIME_ZONE(12),
PRIME(16),
ACTIVATE(18),
SET_BOLUS(19),
CANCEL_BOLUS(20),
SET_BASAL_PROFILE(21),
SET_TEMP_BASAL(24),
CANCEL_TEMP_BASAL(25),
RESUME_PUMP(29),
POLL_PATCH(30),
STOP_PATCH(31),
READ_BOLUS_STATE(34),
SET_PATCH(35),
SET_BOLUS_MOTOR(36),
GET_RECORD(99),
CLEAR_ALARM(115)
}

View file

@ -0,0 +1,34 @@
package info.nightscout.pump.medtrum.comm.enums
enum class MedtrumPumpState(val state: Byte) {
NONE(0),
IDLE(1),
FILLED(2),
PRIMING(3),
PRIMED(4),
EJECTING(5),
EJECTED(6),
ACTIVE(32),
ACTIVE_ALT(33),
LOWBG_SUSPENDED(64),
LOWBG_SUSPENDED2(65),
AUTO_SUSPENDED(66),
HMAX_SUSPENDED(67),
DMAX_SUSPENDED(68),
SUSPENDED(69),
PAUSED(70),
OCCLUSION(96),
EXPIRED(97),
RESERVOIR_EMPTY(98),
PATCH_FAULT(99),
PATCH_FAULT2(100),
BASE_FAULT(101),
BATTERY_OUT(102),
NO_CALIBRATION(103),
STOPPED(128.toByte());
companion object {
fun fromByte(state: Byte) = values().find { it.state == state }
?: throw IllegalAccessException("")
}
}

View file

@ -0,0 +1,122 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.interfaces.pump.DetailedBolusInfo
import info.nightscout.interfaces.pump.PumpSync
import info.nightscout.pump.medtrum.MedtrumPump
import info.nightscout.pump.medtrum.comm.enums.CommandType.ACTIVATE
import info.nightscout.pump.medtrum.comm.enums.BasalType
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.extension.toByte
import info.nightscout.interfaces.stats.TddCalculator
import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.pump.medtrum.extension.toLong
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import javax.inject.Inject
import kotlin.math.round
class ActivatePacket(injector: HasAndroidInjector, private val basalProfile: ByteArray) : MedtrumPacket(injector) {
@Inject lateinit var medtrumPump: MedtrumPump
@Inject lateinit var tddCalculator: TddCalculator
@Inject lateinit var pumpSync: PumpSync
companion object {
private const val RESP_PATCH_ID_START = 6
private const val RESP_PATCH_ID_END = RESP_PATCH_ID_START + 4
private const val RESP_TIME_START = 10
private const val RESP_TIME_END = RESP_TIME_START + 4
private const val RESP_BASAL_TYPE_START = 14
private const val RESP_BASAL_TYPE_END = RESP_BASAL_TYPE_START + 1
private const val RESP_BASAL_VALUE_START = 15
private const val RESP_BASAL_VALUE_END = RESP_BASAL_VALUE_START + 2
private const val RESP_BASAL_SEQUENCE_START = 17
private const val RESP_BASAL_SEQUENCE_END = RESP_BASAL_SEQUENCE_START + 2
private const val RESP_BASAL_PATCH_ID_START = 19
private const val RESP_BASAL_PATCH_ID_END = RESP_BASAL_PATCH_ID_START + 2
private const val RESP_BASAL_START_TIME_START = 21
private const val RESP_BASAL_START_TIME_END = RESP_BASAL_START_TIME_START + 4
}
init {
opCode = ACTIVATE.code
expectedMinRespLength = RESP_BASAL_START_TIME_END
}
override fun getRequest(): ByteArray {
/**
* byte 0: opCode
* byte 1: autoSuspendEnable // Value for auto mode, not used for AAPS
* byte 2: autoSuspendTime // Value for auto mode, not used for AAPS
* byte 3: expirationTimer // Expiration timer, 0 = no expiration 1 = 12 hour reminder and expiration after 3 days
* byte 4: alarmSetting // See AlarmSetting
* byte 5: lowSuspend // Value for auto mode, not used for AAPS
* byte 6: predictiveLowSuspend // Value for auto mode, not used for AAPS
* byte 7: predictiveLowSuspendRange // Value for auto mode, not used for AAPS
* byte 8-9: hourlyMaxInsulin // Max hourly dose of insulin, divided by 0.05
* byte 10-11: daylyMaxSet // Max daily dose of insulin, divided by 0.05
* byte 12-13: tddToday // Current TDD (of present day), divided by 0.05
* byte 14: 1 // Always 1
* bytes 15 - end // Basal profile > see MedtrumPump
*/
val autoSuspendEnable: Byte = 0
val autoSuspendTime: Byte = 12 // Not sure why, but pump needs this in order to activate
val patchExpiration: Byte = medtrumPump.desiredPatchExpiration.toByte()
val alarmSetting: Byte = medtrumPump.desiredAlarmSetting.code
val lowSuspend: Byte = 0
val predictiveLowSuspend: Byte = 0
val predictiveLowSuspendRange: Byte = 30 // Not sure why, but pump needs this in order to activate
val hourlyMaxInsulin: Int = round(medtrumPump.desiredHourlyMaxInsulin / 0.05).toInt()
val dailyMaxInsulin: Int = round(medtrumPump.desiredDailyMaxInsulin / 0.05).toInt()
val currentTDD: Double = tddCalculator.calculateToday()?.totalAmount?.div(0.05) ?: 0.0
return byteArrayOf(opCode) + autoSuspendEnable + autoSuspendTime + patchExpiration + alarmSetting + lowSuspend + predictiveLowSuspend + predictiveLowSuspendRange + hourlyMaxInsulin.toByteArray(
2
) + dailyMaxInsulin.toByteArray(2) + currentTDD.toInt().toByteArray(2) + 1.toByte() + basalProfile
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
val medtrumTimeUtil = MedtrumTimeUtil()
val patchId = data.copyOfRange(RESP_PATCH_ID_START, RESP_PATCH_ID_END).toLong()
val time = medtrumTimeUtil.convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_TIME_START, RESP_TIME_END).toLong())
val basalType = enumValues<BasalType>()[data.copyOfRange(RESP_BASAL_TYPE_START, RESP_BASAL_TYPE_END).toInt()]
val basalValue = data.copyOfRange(RESP_BASAL_VALUE_START, RESP_BASAL_VALUE_END).toInt() * 0.05
val basalSequence = data.copyOfRange(RESP_BASAL_SEQUENCE_START, RESP_BASAL_SEQUENCE_END).toInt()
val basalPatchId = data.copyOfRange(RESP_BASAL_PATCH_ID_START, RESP_BASAL_PATCH_ID_END).toLong()
val basalStartTime = medtrumTimeUtil.convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_BASAL_START_TIME_START, RESP_BASAL_START_TIME_END).toLong())
medtrumPump.patchId = patchId
medtrumPump.lastTimeReceivedFromPump = time
medtrumPump.currentSequenceNumber = basalSequence // We are activated, set the new seq nr
medtrumPump.syncedSequenceNumber = basalSequence // We are activated, reset the synced seq nr ()
// Sync canula change
pumpSync.insertTherapyEventIfNewWithTimestamp(
timestamp = System.currentTimeMillis(),
type = DetailedBolusInfo.EventType.CANNULA_CHANGE,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
pumpSync.insertTherapyEventIfNewWithTimestamp(
timestamp = System.currentTimeMillis(),
type = DetailedBolusInfo.EventType.INSULIN_CHANGE,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
// Update the actual basal profile
medtrumPump.actualBasalProfile = basalProfile
medtrumPump.handleBasalStatusUpdate(basalType, basalValue, basalSequence, basalPatchId, basalStartTime, time)
}
return success
}
}

View file

@ -0,0 +1,57 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumPump
import info.nightscout.pump.medtrum.comm.enums.CommandType.AUTH_REQ
import info.nightscout.pump.medtrum.encryption.Crypt
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.rx.logging.LTag
import javax.inject.Inject
class AuthorizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
@Inject lateinit var medtrumPump: MedtrumPump
companion object {
private const val RESP_DEVICE_TYPE_START = 7
private const val RESP_DEVICE_TYPE_END = RESP_DEVICE_TYPE_START + 1
private const val RESP_VERSION_X_START = 8
private const val RESP_VERSION_X_END = RESP_VERSION_X_START + 1
private const val RESP_VERSION_Y_START = 9
private const val RESP_VERSION_Y_END = RESP_VERSION_Y_START + 1
private const val RESP_VERSION_Z_START = 10
private const val RESP_VERSION_Z_END = RESP_VERSION_Z_START + 1
}
init {
opCode = AUTH_REQ.code
expectedMinRespLength = RESP_VERSION_Z_END
}
override fun getRequest(): ByteArray {
val role = 2 // Fixed to 2 for pump
val key = Crypt().keyGen(medtrumPump.pumpSN)
return byteArrayOf(opCode) + byteArrayOf(role.toByte()) + medtrumPump.patchSessionToken.toByteArray(4) + key.toByteArray(4)
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
val deviceType = data.copyOfRange(RESP_DEVICE_TYPE_START, RESP_DEVICE_TYPE_END).toInt()
val swVersion = "" + data.copyOfRange(RESP_VERSION_X_START, RESP_VERSION_X_END).toInt() + "." + data.copyOfRange(RESP_VERSION_Y_START, RESP_VERSION_Y_END).toInt() + "." + data.copyOfRange(
RESP_VERSION_Z_START, RESP_VERSION_Z_END
).toInt()
if (medtrumPump.deviceType != deviceType) {
medtrumPump.deviceType = deviceType
}
if (medtrumPump.swVersion != swVersion) {
medtrumPump.swVersion = swVersion
}
aapsLogger.debug(LTag.PUMPCOMM, "GetDeviceTypeState: deviceType: ${deviceType}, swVersion: ${swVersion}")
}
return success
}
}

View file

@ -0,0 +1,20 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.enums.CommandType.CANCEL_BOLUS
class CancelBolusPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = CANCEL_BOLUS.code
}
override fun getRequest(): ByteArray {
// Bolus types:
// 1 = normal
// 2 = Extended
// 3 = Combi
val bolusType: Byte = 1 // Only support for normal bolus for now
return byteArrayOf(opCode) + bolusType
}
}

View file

@ -0,0 +1,51 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumPump
import info.nightscout.pump.medtrum.comm.enums.CommandType.CANCEL_TEMP_BASAL
import info.nightscout.pump.medtrum.comm.enums.BasalType
import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.pump.medtrum.extension.toLong
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.utils.DateUtil
import javax.inject.Inject
class CancelTempBasalPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
@Inject lateinit var medtrumPump: MedtrumPump
@Inject lateinit var dateUtil: DateUtil
companion object {
private const val RESP_BASAL_TYPE_START = 6
private const val RESP_BASAL_TYPE_END = RESP_BASAL_TYPE_START + 1
private const val RESP_BASAL_RATE_START = RESP_BASAL_TYPE_END
private const val RESP_BASAL_RATE_END = RESP_BASAL_RATE_START + 2
private const val RESP_BASAL_SEQUENCE_START = RESP_BASAL_RATE_END
private const val RESP_BASAL_SEQUENCE_END = RESP_BASAL_SEQUENCE_START + 2
private const val RESP_BASAL_PATCH_ID_START = RESP_BASAL_SEQUENCE_END
private const val RESP_BASAL_PATCH_ID_END = RESP_BASAL_PATCH_ID_START + 2
private const val RESP_BASAL_START_TIME_START = RESP_BASAL_PATCH_ID_END
private const val RESP_BASAL_START_TIME_END = RESP_BASAL_START_TIME_START + 4
}
init {
opCode = CANCEL_TEMP_BASAL.code
expectedMinRespLength = RESP_BASAL_START_TIME_END
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
val basalType = enumValues<BasalType>()[data.copyOfRange(RESP_BASAL_TYPE_START, RESP_BASAL_TYPE_END).toInt()]
val basalRate = data.copyOfRange(RESP_BASAL_RATE_START, RESP_BASAL_RATE_END).toInt() * 0.05
val basalSequence = data.copyOfRange(RESP_BASAL_SEQUENCE_START, RESP_BASAL_SEQUENCE_END).toInt()
val basalPatchId = data.copyOfRange(RESP_BASAL_PATCH_ID_START, RESP_BASAL_PATCH_ID_END).toLong()
val basalStartTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_BASAL_START_TIME_START, RESP_BASAL_START_TIME_END).toLong())
medtrumPump.handleBasalStatusUpdate(basalType, basalRate, basalSequence, basalPatchId, basalStartTime)
}
return success
}
}

View file

@ -0,0 +1,16 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.enums.CommandType.CLEAR_ALARM
import info.nightscout.pump.medtrum.extension.toByteArray
class ClearPumpAlarmPacket(injector: HasAndroidInjector, val clearType: Int) : MedtrumPacket(injector) {
init {
opCode = CLEAR_ALARM.code
}
override fun getRequest(): ByteArray {
return byteArrayOf(opCode) + clearType.toByteArray(2)
}
}

View file

@ -0,0 +1,35 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.enums.CommandType.GET_DEVICE_TYPE
import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.pump.medtrum.extension.toLong
class GetDeviceTypePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
var deviceType: Int = 0
var deviceSN: Long = 0
companion object {
private const val RESP_DEVICE_TYPE_START = 6
private const val RESP_DEVICE_TYPE_END = RESP_DEVICE_TYPE_START + 1
private const val RESP_DEVICE_SN_START = 7
private const val RESP_DEVICE_SN_END = RESP_DEVICE_SN_START + 4
}
init {
opCode = GET_DEVICE_TYPE.code
expectedMinRespLength = RESP_DEVICE_SN_END
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
deviceType = data.copyOfRange(RESP_DEVICE_TYPE_START, RESP_DEVICE_TYPE_END).toInt()
deviceSN = data.copyOfRange(RESP_DEVICE_SN_START, RESP_DEVICE_SN_END).toLong()
}
return success
}
}

View file

@ -0,0 +1,353 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.interfaces.pump.DetailedBolusInfoStorage
import info.nightscout.interfaces.pump.PumpSync
import info.nightscout.interfaces.pump.TemporaryBasalStorage
import info.nightscout.pump.medtrum.MedtrumPump
import info.nightscout.pump.medtrum.comm.enums.CommandType.GET_RECORD
import info.nightscout.pump.medtrum.comm.enums.BasalType
import info.nightscout.pump.medtrum.comm.enums.BolusType
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.extension.toFloat
import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.pump.medtrum.extension.toLong
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T
import javax.inject.Inject
class GetRecordPacket(injector: HasAndroidInjector, private val recordIndex: Int) : MedtrumPacket(injector) {
@Inject lateinit var medtrumPump: MedtrumPump
@Inject lateinit var pumpSync: PumpSync
@Inject lateinit var temporaryBasalStorage: TemporaryBasalStorage
@Inject lateinit var detailedBolusInfoStorage: DetailedBolusInfoStorage
@Inject lateinit var dateUtil: DateUtil
companion object {
private const val RESP_RECORD_HEADER_START = 6
private const val RESP_RECORD_HEADER_END = RESP_RECORD_HEADER_START + 1
private const val RESP_RECORD_UNKNOWN_START = RESP_RECORD_HEADER_END
private const val RESP_RECORD_UNKNOWN_END = RESP_RECORD_UNKNOWN_START + 1
private const val RESP_RECORD_TYPE_START = RESP_RECORD_UNKNOWN_END
private const val RESP_RECORD_TYPE_END = RESP_RECORD_TYPE_START + 1
private const val RESP_RECORD_UNKNOWN1_START = RESP_RECORD_TYPE_END
private const val RESP_RECORD_UNKNOWN1_END = RESP_RECORD_UNKNOWN1_START + 1
private const val RESP_RECORD_SERIAL_START = RESP_RECORD_UNKNOWN1_END
private const val RESP_RECORD_SERIAL_END = RESP_RECORD_SERIAL_START + 4
private const val RESP_RECORD_PATCHID_START = RESP_RECORD_SERIAL_END
private const val RESP_RECORD_PATCHID_END = RESP_RECORD_PATCHID_START + 2
private const val RESP_RECORD_SEQUENCE_START = RESP_RECORD_PATCHID_END
private const val RESP_RECORD_SEQUENCE_END = RESP_RECORD_SEQUENCE_START + 2
private const val RESP_RECORD_DATA_START = RESP_RECORD_SEQUENCE_END
private const val VALID_HEADER = 170
private const val BOLUS_RECORD = 1
private const val BOLUS_RECORD_ALT = 65
private const val BASAL_RECORD = 2
private const val BASAL_RECORD_ALT = 66
private const val ALARM_RECORD = 3
private const val AUTO_RECORD = 4
private const val TIME_SYNC_RECORD = 5
private const val AUTO1_RECORD = 6
private const val AUTO2_RECORD = 7
private const val AUTO3_RECORD = 8
private const val TDD_RECORD = 9
}
init {
opCode = GET_RECORD.code
expectedMinRespLength = RESP_RECORD_DATA_START
}
override fun getRequest(): ByteArray {
return byteArrayOf(opCode) + recordIndex.toByteArray(2) + medtrumPump.patchId.toByteArray(2)
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
val recordHeader = data.copyOfRange(RESP_RECORD_HEADER_START, RESP_RECORD_HEADER_END).toInt()
val recordUnknown = data.copyOfRange(RESP_RECORD_UNKNOWN_START, RESP_RECORD_UNKNOWN_END).toInt()
val recordType = data.copyOfRange(RESP_RECORD_TYPE_START, RESP_RECORD_TYPE_END).toInt()
val recordSerial = data.copyOfRange(RESP_RECORD_SERIAL_START, RESP_RECORD_SERIAL_END).toLong()
val recordPatchId = data.copyOfRange(RESP_RECORD_PATCHID_START, RESP_RECORD_PATCHID_END).toInt()
val recordSequence = data.copyOfRange(RESP_RECORD_SEQUENCE_START, RESP_RECORD_SEQUENCE_END).toInt()
aapsLogger.debug(
LTag.PUMPCOMM,
"GetRecordPacket HandleResponse: Record header: $recordHeader, unknown: $recordUnknown, type: $recordType, serial: $recordSerial, patchId: $recordPatchId, " + "sequence: $recordSequence"
)
medtrumPump.syncedSequenceNumber = recordSequence // Assume sync upwards
if (recordHeader == VALID_HEADER) {
when (recordType) {
BOLUS_RECORD, BOLUS_RECORD_ALT -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BOLUS_RECORD")
val typeAndWizard = data.copyOfRange(RESP_RECORD_DATA_START, RESP_RECORD_DATA_START + 1).toInt()
val bolusCause = data.copyOfRange(RESP_RECORD_DATA_START + 1, RESP_RECORD_DATA_START + 2).toInt()
val unknown = data.copyOfRange(RESP_RECORD_DATA_START + 2, RESP_RECORD_DATA_START + 4).toInt()
val bolusStartTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_RECORD_DATA_START + 4, RESP_RECORD_DATA_START + 8).toLong())
val bolusNormalAmount = data.copyOfRange(RESP_RECORD_DATA_START + 8, RESP_RECORD_DATA_START + 10).toInt() * 0.05
val bolusNormalDelivered = data.copyOfRange(RESP_RECORD_DATA_START + 10, RESP_RECORD_DATA_START + 12).toInt() * 0.05
val bolusExtendedAmount = data.copyOfRange(RESP_RECORD_DATA_START + 12, RESP_RECORD_DATA_START + 14).toInt() * 0.05
val bolusExtendedDuration = data.copyOfRange(RESP_RECORD_DATA_START + 14, RESP_RECORD_DATA_START + 16).toLong() * 1000
val bolusExtendedDelivered = data.copyOfRange(RESP_RECORD_DATA_START + 16, RESP_RECORD_DATA_START + 18).toInt() * 0.05
val bolusCarb = data.copyOfRange(RESP_RECORD_DATA_START + 18, RESP_RECORD_DATA_START + 20).toInt()
val bolusGlucose = data.copyOfRange(RESP_RECORD_DATA_START + 20, RESP_RECORD_DATA_START + 22).toInt()
val bolusIOB = data.copyOfRange(RESP_RECORD_DATA_START + 22, RESP_RECORD_DATA_START + 24).toInt()
val unkown1 = data.copyOfRange(RESP_RECORD_DATA_START + 24, RESP_RECORD_DATA_START + 26).toInt()
val unkown2 = data.copyOfRange(RESP_RECORD_DATA_START + 26, RESP_RECORD_DATA_START + 28).toInt()
val bolusType = enumValues<BolusType>()[typeAndWizard and 0x0F]
val bolusWizard = (typeAndWizard and 0xF0) != 0
aapsLogger.debug(
LTag.PUMPCOMM,
"GetRecordPacket HandleResponse: BOLUS_RECORD: typeAndWizard: $typeAndWizard, bolusCause: $bolusCause, unknown: $unknown, bolusStartTime: $bolusStartTime, " + "bolusNormalAmount: $bolusNormalAmount, bolusNormalDelivered: $bolusNormalDelivered, bolusExtendedAmount: $bolusExtendedAmount, bolusExtendedDuration: $bolusExtendedDuration, " + "bolusExtendedDelivered: $bolusExtendedDelivered, bolusCarb: $bolusCarb, bolusGlucose: $bolusGlucose, bolusIOB: $bolusIOB, unkown1: $unkown1, unkown2: $unkown2, " + "bolusType: $bolusType, bolusWizard: $bolusWizard"
)
if (bolusType == BolusType.NORMAL) {
val detailedBolusInfo = detailedBolusInfoStorage.findDetailedBolusInfo(bolusStartTime, bolusNormalDelivered)
var newRecord = false
if (detailedBolusInfo != null) {
val syncOk = pumpSync.syncBolusWithTempId(
timestamp = bolusStartTime,
amount = bolusNormalDelivered,
temporaryId = detailedBolusInfo.timestamp,
type = detailedBolusInfo.bolusType,
pumpId = bolusStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
if (syncOk == false) {
aapsLogger.warn(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BOLUS_RECORD: Failed to sync bolus with tempId: ${detailedBolusInfo.timestamp}")
// detailedInfo can be from another similar record. Reinsert
detailedBolusInfoStorage.add(detailedBolusInfo)
}
} else {
newRecord = pumpSync.syncBolusWithPumpId(
timestamp = bolusStartTime,
amount = bolusNormalDelivered,
type = null,
pumpId = bolusStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
}
aapsLogger.debug(
LTag.PUMPCOMM,
"from record: ${if (newRecord) "**NEW** " else ""}EVENT BOLUS ${dateUtil.dateAndTimeString(bolusStartTime)} ($bolusStartTime) Bolus: ${bolusNormalDelivered}U "
)
if (bolusStartTime > medtrumPump.lastBolusTime) {
medtrumPump.lastBolusTime = bolusStartTime
medtrumPump.lastBolusAmount = bolusNormalDelivered
}
} else if (bolusType == BolusType.EXTENDED) {
val newRecord = pumpSync.syncExtendedBolusWithPumpId(
timestamp = bolusStartTime,
amount = bolusExtendedDelivered,
duration = bolusExtendedDuration,
isEmulatingTB = false,
pumpId = bolusStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
aapsLogger.debug(
LTag.PUMPCOMM,
"from record: ${if (newRecord) "**NEW** " else ""}EVENT EXTENDED BOLUS ${dateUtil.dateAndTimeString(bolusStartTime)} ($bolusStartTime) Bolus: ${bolusNormalDelivered}U "
)
} else if (bolusType == BolusType.COMBI) {
// Note, this should never happen, as we don't use combo bolus
val detailedBolusInfo = detailedBolusInfoStorage.findDetailedBolusInfo(bolusStartTime, bolusNormalDelivered)
val newRecord = pumpSync.syncBolusWithPumpId(
timestamp = bolusStartTime,
amount = bolusNormalDelivered,
type = detailedBolusInfo?.bolusType,
pumpId = bolusStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
pumpSync.syncExtendedBolusWithPumpId(
timestamp = bolusStartTime,
amount = bolusExtendedDelivered,
duration = bolusExtendedDuration,
isEmulatingTB = false,
pumpId = bolusStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
aapsLogger.error(
LTag.PUMPCOMM,
"from record: ${if (newRecord) "**NEW** " else ""}EVENT COMBI BOLUS ${dateUtil.dateAndTimeString(bolusStartTime)} ($bolusStartTime) Bolus: ${bolusNormalDelivered}U Extended: ${bolusExtendedDelivered} THIS SHOULD NOT HAPPEN!!!"
)
if (!newRecord && detailedBolusInfo != null) {
// detailedInfo can be from another similar record. Reinsert
detailedBolusInfoStorage.add(detailedBolusInfo)
}
if (bolusStartTime > medtrumPump.lastBolusTime) {
medtrumPump.lastBolusTime = bolusStartTime
medtrumPump.lastBolusAmount = bolusNormalDelivered
}
} else {
aapsLogger.error(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BOLUS_RECORD: Unknown bolus type: $bolusType")
}
}
BASAL_RECORD, BASAL_RECORD_ALT -> {
val medtrumTimeUtil = MedtrumTimeUtil()
val basalStartTime = medtrumTimeUtil.convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_RECORD_DATA_START, RESP_RECORD_DATA_START + 4).toLong())
val basalEndTime = medtrumTimeUtil.convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_RECORD_DATA_START + 4, RESP_RECORD_DATA_START + 8).toLong())
val basalType = enumValues<BasalType>()[data.copyOfRange(RESP_RECORD_DATA_START + 8, RESP_RECORD_DATA_START + 9).toInt()]
val basalEndReason = data.copyOfRange(RESP_RECORD_DATA_START + 9, RESP_RECORD_DATA_START + 10).toInt()
val basalRate = data.copyOfRange(RESP_RECORD_DATA_START + 10, RESP_RECORD_DATA_START + 12).toInt() * 0.05
val basalDelivered = data.copyOfRange(RESP_RECORD_DATA_START + 12, RESP_RECORD_DATA_START + 14).toInt() * 0.05
val basalPercent = data.copyOfRange(RESP_RECORD_DATA_START + 14, RESP_RECORD_DATA_START + 16).toInt()
aapsLogger.debug(
LTag.PUMPCOMM,
"GetRecordPacket HandleResponse: BASAL_RECORD: Start: $basalStartTime, End: $basalEndTime, Type: $basalType, EndReason: $basalEndReason, Rate: $basalRate, Delivered: $basalDelivered, Percent: $basalPercent"
)
when (basalType) {
BasalType.STANDARD -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BASAL_RECORD: Standard basal")
// If we are here it means the basal has ended
}
BasalType.ABSOLUTE_TEMP, BasalType.RELATIVE_TEMP -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BASAL_RECORD: Absolute temp basal")
var duration = (basalEndTime - basalStartTime)
// Work around for pumpSync not accepting 0 duration.
// sometimes we get 0 duration for very short basal because the pump only reports time in seconds
if (duration < 250) duration = 250 // 250ms to make sure AAPS accepts it
val newRecord = pumpSync.syncTemporaryBasalWithPumpId(
timestamp = basalStartTime,
rate = if (basalType == BasalType.ABSOLUTE_TEMP) basalRate else basalPercent.toDouble(),
duration = duration,
isAbsolute = (basalType == BasalType.ABSOLUTE_TEMP),
type = PumpSync.TemporaryBasalType.NORMAL,
pumpId = basalStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
aapsLogger.debug(
LTag.PUMPCOMM,
"handleBasalStatusUpdate from record: ${if (newRecord) "**NEW** " else ""}EVENT TEMP_SYNC: ($basalType) ${dateUtil.dateAndTimeString(basalStartTime)} ($basalStartTime) " +
"Rate: $basalRate Duration: ${duration}"
)
}
in BasalType.SUSPEND_LOW_GLUCOSE..BasalType.STOP -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BASAL_RECORD: Suspend basal")
val duration = (basalEndTime - basalStartTime)
val newRecord = pumpSync.syncTemporaryBasalWithPumpId(
timestamp = basalEndTime,
rate = 0.0,
duration = duration,
isAbsolute = true,
type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
pumpId = basalStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
aapsLogger.debug(
LTag.PUMPCOMM,
"handleBasalStatusUpdate from record: ${if (newRecord) "**NEW** " else ""}EVENT SUSPEND: ($basalType) ${dateUtil.dateAndTimeString(basalStartTime)} ($basalStartTime) " +
"Rate: $basalRate Duration: ${duration}"
)
}
else -> {
aapsLogger.error(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BASAL_RECORD: Unknown basal type: $basalType")
}
}
}
ALARM_RECORD -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: ALARM_RECORD")
}
AUTO_RECORD -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: AUTO_RECORD")
}
TIME_SYNC_RECORD -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: TIME_SYNC_RECORD")
}
AUTO1_RECORD -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: AUTO1_RECORD")
}
AUTO2_RECORD -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: AUTO2_RECORD")
}
AUTO3_RECORD -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: AUTO3_RECORD")
}
TDD_RECORD -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: TDD_RECORD")
val timestamp = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_RECORD_DATA_START, RESP_RECORD_DATA_START + 4).toLong())
val timeZoneOffset = data.copyOfRange(RESP_RECORD_DATA_START + 4, RESP_RECORD_DATA_START + 6).toInt()
val tddMins = data.copyOfRange(RESP_RECORD_DATA_START + 6, RESP_RECORD_DATA_START + 8).toInt()
val glucoseRecordTime = data.copyOfRange(RESP_RECORD_DATA_START + 8, RESP_RECORD_DATA_START + 12).toLong()
val tdd = data.copyOfRange(RESP_RECORD_DATA_START + 12, RESP_RECORD_DATA_START + 16).toFloat()
val basalTdd = data.copyOfRange(RESP_RECORD_DATA_START + 16, RESP_RECORD_DATA_START + 20).toFloat()
val glucose = data.copyOfRange(RESP_RECORD_DATA_START + 20, RESP_RECORD_DATA_START + 24).toFloat()
val unknown = data.copyOfRange(RESP_RECORD_DATA_START + 24, RESP_RECORD_DATA_START + 28).toFloat()
val meanSomething = data.copyOfRange(RESP_RECORD_DATA_START + 28, RESP_RECORD_DATA_START + 32).toFloat()
val usedTdd = data.copyOfRange(RESP_RECORD_DATA_START + 32, RESP_RECORD_DATA_START + 36).toFloat()
val usedIBasal = data.copyOfRange(RESP_RECORD_DATA_START + 36, RESP_RECORD_DATA_START + 40).toFloat()
val usedSgBasal = data.copyOfRange(RESP_RECORD_DATA_START + 40, RESP_RECORD_DATA_START + 44).toFloat()
val usedUMax = data.copyOfRange(RESP_RECORD_DATA_START + 44, RESP_RECORD_DATA_START + 48).toFloat()
val newTdd = data.copyOfRange(RESP_RECORD_DATA_START + 48, RESP_RECORD_DATA_START + 52).toFloat()
val newIBasal = data.copyOfRange(RESP_RECORD_DATA_START + 52, RESP_RECORD_DATA_START + 56).toFloat()
val newSgBasal = data.copyOfRange(RESP_RECORD_DATA_START + 56, RESP_RECORD_DATA_START + 60).toFloat()
val newUMax = data.copyOfRange(RESP_RECORD_DATA_START + 60, RESP_RECORD_DATA_START + 64).toFloat()
aapsLogger.debug(
LTag.PUMPCOMM, "TDD_RECORD: timestamp: $timestamp, timeZoneOffset: $timeZoneOffset, tddMins: $tddMins, glucoseRecordTime: $glucoseRecordTime, tdd: " +
"$tdd, basalTdd: $basalTdd, glucose: $glucose, unknown: $unknown, meanSomething: $meanSomething, usedTdd: $usedTdd, usedIBasal: $usedIBasal, usedSgBasal: " +
"$usedSgBasal, usedUMax: $usedUMax, newTdd: $newTdd, newIBasal: $newIBasal, newSgBasal: $newSgBasal, newUMax: $newUMax"
)
val newRecord = pumpSync.createOrUpdateTotalDailyDose(
timestamp = timestamp,
bolusAmount = (tdd - basalTdd).toDouble(),
basalAmount = basalTdd.toDouble(),
totalAmount = tdd.toDouble(),
pumpId = timestamp,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
aapsLogger.debug(
LTag.PUMPCOMM,
"handleBasalStatusUpdate from record: ${if (newRecord) "**NEW** " else ""}EVENT TDD: ${dateUtil.dateAndTimeString(timestamp)} ($timestamp) " +
"TDD: $tdd, BasalTDD: $basalTdd, BolusTDD: ${tdd - basalTdd}"
)
}
else -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: Unknown record type: $recordType")
}
}
} else {
aapsLogger.error(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: Invalid record header")
}
}
return success
}
}

View file

@ -0,0 +1,34 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumPump
import info.nightscout.pump.medtrum.comm.enums.CommandType.GET_TIME
import info.nightscout.pump.medtrum.extension.toLong
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import javax.inject.Inject
class GetTimePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
@Inject lateinit var medtrumPump: MedtrumPump
companion object {
private const val RESP_TIME_START = 6
private const val RESP_TIME_END = RESP_TIME_START + 4
}
init {
opCode = GET_TIME.code
expectedMinRespLength = RESP_TIME_END
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
val time = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_TIME_START, RESP_TIME_END).toLong())
medtrumPump.lastTimeReceivedFromPump = time
}
return success
}
}

View file

@ -0,0 +1,79 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import javax.inject.Inject
open class MedtrumPacket(protected var injector: HasAndroidInjector) {
@Inject lateinit var aapsLogger: AAPSLogger
var opCode: Byte = 0
var failed = false
var expectedMinRespLength = RESP_RESULT_END
companion object {
const val RESP_OPCODE_START = 1
const val RESP_OPCODE_END = RESP_OPCODE_START + 1
const val RESP_RESULT_START = 4
const val RESP_RESULT_END = RESP_RESULT_START + 2
private const val RESP_WAITING = 16384
}
init {
// @Suppress("LeakingThis")
injector.androidInjector().inject(this)
}
open fun getRequest(): ByteArray {
return byteArrayOf(opCode)
}
/** handles a response from the Medtrum pump, returns true if command was successfull, returns false if command failed or waiting for response */
open fun handleResponse(data: ByteArray): Boolean {
// Check for broken packets
if (RESP_RESULT_END > data.size) {
failed = true
aapsLogger.debug(LTag.PUMPCOMM, "handleResponse: Unexpected response length, expected: $expectedMinRespLength got: ${data.size}")
return false
}
val incomingOpCode: Byte = data.copyOfRange(RESP_OPCODE_START, RESP_OPCODE_END).first()
val responseCode = data.copyOfRange(RESP_RESULT_START, RESP_RESULT_END).toInt()
return when {
incomingOpCode != opCode -> {
failed = true
aapsLogger.error(LTag.PUMPCOMM, "handleResponse: Unexpected command, expected: $opCode got: $incomingOpCode")
false
}
responseCode == 0 -> {
// Check if length is what is expected from this type of packet
if (expectedMinRespLength > data.size) {
failed = true
aapsLogger.debug(LTag.PUMPCOMM, "handleResponse: Unexpected response length, expected: $expectedMinRespLength got: ${data.size}")
return false
}
aapsLogger.debug(LTag.PUMPCOMM, "handleResponse: Happy command: $opCode response: $responseCode")
true
}
responseCode == RESP_WAITING -> {
aapsLogger.debug(LTag.PUMPCOMM, "handleResponse: Waiting command: $opCode response: $responseCode")
// Waiting do nothing
false
}
else -> {
failed = true
aapsLogger.warn(LTag.PUMPCOMM, "handleResponse: Error in command: $opCode response: $responseCode")
false
}
}
}
}

View file

@ -0,0 +1,236 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumPump
import info.nightscout.pump.medtrum.comm.enums.AlarmState
import info.nightscout.pump.medtrum.comm.enums.BasalType
import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState
import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.pump.medtrum.extension.toLong
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import javax.inject.Inject
class NotificationPacket(val injector: HasAndroidInjector) {
/**
* This is a bit of a special packet, as it is not a command packet
* but a notification packet. It is sent by the pump to the phone
* when the pump has a notification to send.
*
* Notifications are sent regualary, regardless of the pump state.
*
* There can be multiple messages in one packet, this is noted by the fieldMask.
*
* Byte 1: State (Handle a state change directly? before analyzing further?)
* Byte 2-3: FieldMask (BitMask which tells the fields present in the message)
* Byte 4-end : status data
*
* When multiple fields are in the message, the data is concatenated.
* This kind of message can also come as a response of SynchronizePacket,
* and can be handled here by handleMaskedMessage() as well.
*/
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var medtrumPump: MedtrumPump
companion object {
private const val NOTIF_STATE_START = 0
private const val NOTIF_STATE_END = NOTIF_STATE_START + 1
private const val MASK_SUSPEND = 0x01
private const val MASK_NORMAL_BOLUS = 0x02
private const val MASK_EXTENDED_BOLUS = 0x04
private const val MASK_BASAL = 0x08
private const val MASK_SETUP = 0x10
private const val MASK_RESERVOIR = 0x20
private const val MASK_START_TIME = 0x40
private const val MASK_BATTERY = 0x80
private const val MASK_STORAGE = 0x100
private const val MASK_ALARM = 0x200
private const val MASK_AGE = 0x400
private const val MASK_UNKNOWN_1 = 0x800
private const val MASK_UNUSED_CGM = 0x1000
private const val MASK_UNUSED_COMMAND_CONFIRM = 0x2000
private const val MASK_UNUSED_AUTO_STATUS = 0x4000
private const val MASK_UNUSED_LEGACY = 0x8000
}
init {
injector.androidInjector().inject(this)
}
fun handleNotification(notification: ByteArray) {
val state = MedtrumPumpState.fromByte(notification[0])
aapsLogger.debug(LTag.PUMPCOMM, "Notification state: $state, current state: ${medtrumPump.pumpState}")
if (state != medtrumPump.pumpState) {
aapsLogger.debug(LTag.PUMPCOMM, "State changed from ${medtrumPump.pumpState} to $state")
medtrumPump.pumpState = state
}
if (notification.size > NOTIF_STATE_END) {
handleMaskedMessage(notification.copyOfRange(NOTIF_STATE_END, notification.size))
}
}
/**
* Handle a message with a field mask, can be used by other packets as well
*/
fun handleMaskedMessage(data: ByteArray) {
val fieldMask = data.copyOfRange(0, 2).toInt()
var offset = 2
aapsLogger.debug(LTag.PUMPCOMM, "Message field mask: $fieldMask")
if (fieldMask and MASK_SUSPEND != 0) {
aapsLogger.debug(LTag.PUMPCOMM, "Suspend notification received")
medtrumPump.suspendTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(offset, offset + 4).toLong())
aapsLogger.debug(LTag.PUMPCOMM, "Suspend time: ${medtrumPump.suspendTime}")
offset += 4
}
if (fieldMask and MASK_NORMAL_BOLUS != 0) {
aapsLogger.debug(LTag.PUMPCOMM, "Normal bolus notification received")
var bolusData = data.copyOfRange(offset, offset + 1).toInt()
var bolusType = bolusData and 0x7F
val bolusCompleted: Boolean = ((bolusData shr 7) and 0x01) != 0
var bolusDelivered = data.copyOfRange(offset + 1, offset + 3).toInt() * 0.05
aapsLogger.debug(LTag.PUMPCOMM, "Bolus type: $bolusType, bolusData: $bolusData bolus completed: $bolusCompleted, bolus delivered: $bolusDelivered")
medtrumPump.handleBolusStatusUpdate(bolusType, bolusCompleted, bolusDelivered)
offset += 3
}
if (fieldMask and MASK_EXTENDED_BOLUS != 0) {
aapsLogger.error(LTag.PUMPCOMM, "Extended bolus notification received, extended bolus not supported!")
// TODO Handle error and stop pump if this happens?
offset += 3
}
if (fieldMask and MASK_BASAL != 0) {
aapsLogger.debug(LTag.PUMPCOMM, "Basal notification received")
val basalType = enumValues<BasalType>()[data.copyOfRange(offset, offset + 1).toInt()]
var basalSequence = data.copyOfRange(offset + 1, offset + 3).toInt()
var basalPatchId = data.copyOfRange(offset + 3, offset + 5).toLong()
var basalStartTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(offset + 5, offset + 9).toLong())
var basalRateAndDelivery = data.copyOfRange(offset + 9, offset + 12).toInt()
var basalRate = (basalRateAndDelivery and 0xFFF) * 0.05
var basalDelivery = (basalRateAndDelivery shr 12) * 0.05
aapsLogger.debug(
LTag.PUMPCOMM,
"Basal type: $basalType, basal sequence: $basalSequence, basal patch id: $basalPatchId, basal time: $basalStartTime, basal rate: $basalRate, basal delivery: $basalDelivery"
)
// Don't spam with basal updates here, only if the running basal rate has changed, or a new basal is set
if (medtrumPump.lastBasalRate != basalRate || medtrumPump.lastBasalStartTime != basalStartTime) {
medtrumPump.handleBasalStatusUpdate(basalType, basalRate, basalSequence, basalPatchId, basalStartTime)
}
offset += 12
}
if (fieldMask and MASK_SETUP != 0) {
aapsLogger.debug(LTag.PUMPCOMM, "Setup notification received")
medtrumPump.primeProgress = data.copyOfRange(offset, offset + 1).toInt()
aapsLogger.debug(LTag.PUMPCOMM, "Prime progress: ${medtrumPump.primeProgress}")
offset += 1
}
if (fieldMask and MASK_RESERVOIR != 0) {
aapsLogger.debug(LTag.PUMPCOMM, "Reservoir notification received")
medtrumPump.reservoir = data.copyOfRange(offset, offset + 2).toInt() * 0.05
aapsLogger.debug(LTag.PUMPCOMM, "Reservoir: ${medtrumPump.reservoir}")
offset += 2
}
if (fieldMask and MASK_START_TIME != 0) {
aapsLogger.debug(LTag.PUMPCOMM, "Start time notification received")
val patchStartTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(offset, offset + 4).toLong())
if (medtrumPump.patchStartTime != patchStartTime) {
aapsLogger.debug(LTag.PUMPCOMM, "Patch start time changed from ${medtrumPump.patchStartTime} to $patchStartTime")
medtrumPump.patchStartTime = patchStartTime
}
aapsLogger.debug(LTag.PUMPCOMM, "Patch start time: ${patchStartTime}")
offset += 4
}
if (fieldMask and MASK_BATTERY != 0) {
aapsLogger.debug(LTag.PUMPCOMM, "Battery notification received")
var parameter = data.copyOfRange(offset, offset + 3).toInt()
// Precision for voltage A is a guess, voltage B is the important one, threshold: < 2.64
medtrumPump.batteryVoltage_A = (parameter and 0xFFF) / 512.0
medtrumPump.batteryVoltage_B = (parameter shr 12) / 512.0
aapsLogger.debug(LTag.PUMPCOMM, "Battery voltage A: ${medtrumPump.batteryVoltage_A}, battery voltage B: ${medtrumPump.batteryVoltage_B}")
offset += 3
}
if (fieldMask and MASK_STORAGE != 0) {
aapsLogger.debug(LTag.PUMPCOMM, "Storage notification received")
val sequence = data.copyOfRange(offset, offset + 2).toInt()
if (sequence > medtrumPump.currentSequenceNumber) {
medtrumPump.currentSequenceNumber = sequence
}
val patchId = data.copyOfRange(offset + 2, offset + 4).toLong()
if (patchId != medtrumPump.patchId) {
aapsLogger.warn(LTag.PUMPCOMM, "handleMaskedMessage: We got wrong patch id!")
}
aapsLogger.debug(LTag.PUMPCOMM, "Last known sequence number: ${medtrumPump.currentSequenceNumber}, patch id: ${patchId}")
offset += 4
}
if (fieldMask and MASK_ALARM != 0) {
val alarmFlags = data.copyOfRange(offset, offset + 2).toInt()
val alarmParameter = data.copyOfRange(offset + 2, offset + 4).toInt()
aapsLogger.debug(LTag.PUMPCOMM, "Alarm notification received, Alarm flags: $alarmFlags, alarm parameter: $alarmParameter")
// If no alarm, clear activeAlarm list
if (alarmFlags == 0 && medtrumPump.activeAlarms.isNotEmpty()) {
medtrumPump.clearAlarmState()
} else if (alarmFlags != 0) {
// Check each alarm bit
for (i in 0..3) { // Only the first 3 flags are interesting for us, the rest we will get from the pump state
val alarmState = AlarmState.values()[i]
if ((alarmFlags shr i) and 1 != 0) {
// If the alarm bit is set, add the corresponding alarm to activeAlarms
medtrumPump.addAlarm(alarmState)
} else if (medtrumPump.activeAlarms.contains(alarmState)) {
// If the alarm bit is not set, and the corresponding alarm is in activeAlarms, remove it
medtrumPump.removeAlarm(alarmState)
}
}
}
offset += 4
}
if (fieldMask and MASK_AGE != 0) {
aapsLogger.debug(LTag.PUMPCOMM, "Age notification received")
medtrumPump.patchAge = data.copyOfRange(offset, offset + 4).toLong()
aapsLogger.debug(LTag.PUMPCOMM, "Patch age: ${medtrumPump.patchAge}")
offset += 4
}
if (fieldMask and MASK_UNKNOWN_1 != 0) {
aapsLogger.debug(LTag.PUMPCOMM, "Unknown 1 notification received, not handled!")
}
if (fieldMask and MASK_UNUSED_CGM != 0) {
aapsLogger.debug(LTag.PUMPCOMM, "Unused CGM notification received, not handled!")
}
if (fieldMask and MASK_UNUSED_COMMAND_CONFIRM != 0) {
// This one is a warning, as this happens we need to know about it, and maybe implement
aapsLogger.warn(LTag.PUMPCOMM, "Unused command confirm notification received, not handled!")
}
if (fieldMask and MASK_UNUSED_AUTO_STATUS != 0) {
aapsLogger.debug(LTag.PUMPCOMM, "Unused auto status notification received, not handled!")
}
if (fieldMask and MASK_UNUSED_LEGACY != 0) {
aapsLogger.debug(LTag.PUMPCOMM, "Unused legacy notification received, not handled!")
}
}
}

View file

@ -0,0 +1,11 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.enums.CommandType.POLL_PATCH
class PollPatchPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = POLL_PATCH.code
}
}

View file

@ -0,0 +1,11 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.enums.CommandType.PRIME
class PrimePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = PRIME.code
}
}

View file

@ -0,0 +1,31 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.enums.CommandType.READ_BOLUS_STATE
class ReadBolusStatePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
// UNUSED
// Bolus sync is currently done by getting the records and syncing then with AAPS pumpSync there
var bolusData: ByteArray = byteArrayOf()
companion object {
private const val RESP_BOLUS_DATA_START = 6
}
init {
opCode = READ_BOLUS_STATE.code
expectedMinRespLength = RESP_BOLUS_DATA_START + 1
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
// UNUSED
bolusData = data.copyOfRange(RESP_BOLUS_DATA_START, data.size)
}
return success
}
}

View file

@ -0,0 +1,16 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.enums.CommandType.RESUME_PUMP
import info.nightscout.pump.medtrum.extension.toByteArray
class ResumePumpPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = RESUME_PUMP.code
}
override fun getRequest(): ByteArray {
return byteArrayOf(opCode)
}
}

View file

@ -0,0 +1,57 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumPump
import info.nightscout.pump.medtrum.comm.enums.CommandType.SET_BASAL_PROFILE
import info.nightscout.pump.medtrum.comm.enums.BasalType
import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.pump.medtrum.extension.toLong
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import javax.inject.Inject
class SetBasalProfilePacket(injector: HasAndroidInjector, private val basalProfile: ByteArray) : MedtrumPacket(injector) {
@Inject lateinit var medtrumPump: MedtrumPump
companion object {
private const val RESP_BASAL_TYPE_START = 6
private const val RESP_BASAL_TYPE_END = RESP_BASAL_TYPE_START + 1
private const val RESP_BASAL_VALUE_START = 7
private const val RESP_BASAL_VALUE_END = RESP_BASAL_VALUE_START + 2
private const val RESP_BASAL_SEQUENCE_START = 9
private const val RESP_BASAL_SEQUENCE_END = RESP_BASAL_SEQUENCE_START + 2
private const val RESP_BASAL_PATCH_ID_START = 11
private const val RESP_BASAL_PATCH_ID_END = RESP_BASAL_PATCH_ID_START + 2
private const val RESP_BASAL_START_TIME_START = 13
private const val RESP_BASAL_START_TIME_END = RESP_BASAL_START_TIME_START + 4
}
init {
opCode = SET_BASAL_PROFILE.code
expectedMinRespLength = RESP_BASAL_START_TIME_END
}
override fun getRequest(): ByteArray {
val basalType: Byte = 1 // Fixed to normal basal
return byteArrayOf(opCode) + basalType + basalProfile
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
val medtrumTimeUtil = MedtrumTimeUtil()
val basalType = enumValues<BasalType>()[data.copyOfRange(RESP_BASAL_TYPE_START, RESP_BASAL_TYPE_END).toInt()]
val basalValue = data.copyOfRange(RESP_BASAL_VALUE_START, RESP_BASAL_VALUE_END).toInt() * 0.05
val basalSequence = data.copyOfRange(RESP_BASAL_SEQUENCE_START, RESP_BASAL_SEQUENCE_END).toInt()
val basalPatchId = data.copyOfRange(RESP_BASAL_PATCH_ID_START, RESP_BASAL_PATCH_ID_END).toLong()
val basalStartTime = medtrumTimeUtil.convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_BASAL_START_TIME_START, RESP_BASAL_START_TIME_END).toLong())
// Update the actual basal profile
medtrumPump.actualBasalProfile = basalProfile
medtrumPump.handleBasalStatusUpdate(basalType, basalValue, basalSequence, basalPatchId, basalStartTime)
}
return success
}
}

View file

@ -0,0 +1,17 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.enums.CommandType.SET_BOLUS_MOTOR
class SetBolusMotorPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
// UNUSED in our driver
init {
opCode = SET_BOLUS_MOTOR.code
}
override fun getRequest(): ByteArray {
return byteArrayOf(opCode) + 0.toByte()
}
}

Some files were not shown because too many files have changed in this diff Show more