diff --git a/app-wear-shared/rx/build.gradle b/app-wear-shared/rx/build.gradle index 0a7785a55e..feff93f500 100644 --- a/app-wear-shared/rx/build.gradle +++ b/app-wear-shared/rx/build.gradle @@ -1,9 +1,11 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' -apply plugin: 'kotlinx-serialization' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' + id 'kotlinx-serialization' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" diff --git a/app-wear-shared/shared-impl/build.gradle b/app-wear-shared/shared-impl/build.gradle index aec0dc99b4..ada8320490 100644 --- a/app-wear-shared/shared-impl/build.gradle +++ b/app-wear-shared/shared-impl/build.gradle @@ -1,9 +1,11 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' -apply plugin: 'kotlinx-serialization' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' + id 'kotlinx-serialization' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" diff --git a/app-wear-shared/shared/build.gradle b/app-wear-shared/shared/build.gradle index 43ccc223c5..8556ce1965 100644 --- a/app-wear-shared/shared/build.gradle +++ b/app-wear-shared/shared/build.gradle @@ -1,12 +1,15 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' -apply plugin: 'kotlinx-serialization' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlinx-serialization' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" @@ -31,7 +34,7 @@ dependencies { api 'org.slf4j:slf4j-api:1.7.36' // 2.0.x breaks logging. Code change needed api 'com.github.tony19:logback-android:2.0.0' - api "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1" + api "org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization_version" api "org.apache.commons:commons-lang3:$commonslang3_version" //RxBus diff --git a/app/build.gradle b/app/build.gradle index 07aabedd6a..45ae641bf4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,10 +1,13 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.google.gms.google-services' -apply plugin: 'com.hiya.jacoco-android' -apply plugin: 'com.google.firebase.crashlytics' +plugins { + id 'com.android.application' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' + id 'kotlinx-serialization' + id 'com.google.gms.google-services' + id 'com.google.firebase.crashlytics' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" @@ -105,7 +108,7 @@ android { defaultConfig { multiDexEnabled true versionCode 1500 - version "3.1.0.3-dev-c" + version "3.1.0.3-dev-c-nscv3" buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' @@ -178,6 +181,7 @@ dependencies { // https://github.com/nightscout/iconify.git implementation project(':graphview') implementation project(':libraries') + implementation project(':ns-sdk') implementation project(':app-wear-shared:rx') implementation project(':app-wear-shared:shared') implementation project(':app-wear-shared:shared-impl') diff --git a/app/src/androidTest/java/info/nightscout/androidaps/RealPumpTest.kt b/app/src/androidTest/java/info/nightscout/androidaps/RealPumpTest.kt index 75ae0caa20..43ff3c41e7 100644 --- a/app/src/androidTest/java/info/nightscout/androidaps/RealPumpTest.kt +++ b/app/src/androidTest/java/info/nightscout/androidaps/RealPumpTest.kt @@ -77,7 +77,7 @@ class RealPumpTest { configBuilderPlugin.performPluginSwitch(loopPlugin, true, PluginType.LOOP) // Enable common - configBuilderPlugin.performPluginSwitch(actionsPlugin, true, PluginType.GENERAL) + configBuilderPlugin.performPluginSwitch(actionsPlugin, true, ) // Disable unneeded MainApp.getPluginsList().remove(objectivesPlugin) diff --git a/app/src/androidTest/java/info/nightscout/androidaps/SetupWizardActivityTest.kt b/app/src/androidTest/java/info/nightscout/androidaps/SetupWizardActivityTest.kt index f20eed392a..ba6a794cab 100644 --- a/app/src/androidTest/java/info/nightscout/androidaps/SetupWizardActivityTest.kt +++ b/app/src/androidTest/java/info/nightscout/androidaps/SetupWizardActivityTest.kt @@ -169,7 +169,7 @@ adb shell settings put global animator_duration_scale 0 & Assert.assertEquals(1.1, p.getBasalTimeFromMidnight(0), 0.0001) Assert.assertEquals(6.0 * Constants.MMOLL_TO_MGDL, p.targetLowMgdl, 0.0001) Assert.assertTrue(VirtualPumpPlugin.getPlugin().isEnabled(PluginType.PUMP)) - Assert.assertTrue(OpenAPSSMBPlugin.getPlugin().isEnabled(PluginType.APS)) + Assert.assertTrue(OpenAPSSMBPlugin.getPlugin().isEnabled()) Assert.assertTrue(LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) Assert.assertTrue(SensitivityOref1Plugin.getPlugin().isEnabled(PluginType.SENSITIVITY)) Assert.assertTrue(ObjectivesPlugin.objectives[0].isStarted) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ca9466e1ea..93f9a92669 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -172,7 +172,7 @@ { diff --git a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt index 8d3db01287..759c30d795 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt @@ -36,9 +36,6 @@ import info.nightscout.androidaps.plugins.aps.openAPSSMBDynamicISF.OpenAPSSMBDyn import info.nightscout.androidaps.plugins.configBuilder.PluginStore import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin -import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin -import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus -import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin import info.nightscout.androidaps.plugins.general.wear.WearPlugin import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin import info.nightscout.androidaps.plugins.pump.eopatch.EopatchPumpPlugin @@ -56,6 +53,10 @@ import info.nightscout.androidaps.plugins.source.GlunovoPlugin import info.nightscout.androidaps.plugins.source.IntelligoPlugin import info.nightscout.androidaps.plugins.source.PoctechPlugin import info.nightscout.androidaps.plugins.source.TomatoPlugin +import info.nightscout.androidaps.plugins.sync.nsclient.NSClientPlugin +import info.nightscout.androidaps.plugins.sync.nsclient.data.NSSettingsStatus +import info.nightscout.androidaps.plugins.sync.nsclientV3.NSClientV3Plugin +import info.nightscout.androidaps.plugins.sync.tidepool.TidepoolPlugin import info.nightscout.androidaps.utils.alertDialogs.OKDialog.show import info.nightscout.androidaps.utils.protection.PasswordCheck import info.nightscout.androidaps.utils.protection.ProtectionCheck.ProtectionType.BIOMETRIC @@ -97,6 +98,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang @Inject lateinit var localInsightPlugin: LocalInsightPlugin @Inject lateinit var medtronicPumpPlugin: MedtronicPumpPlugin @Inject lateinit var nsClientPlugin: NSClientPlugin + @Inject lateinit var nsClientV3Plugin: NSClientV3Plugin @Inject lateinit var openAPSAMAPlugin: OpenAPSAMAPlugin @Inject lateinit var openAPSSMBPlugin: OpenAPSSMBPlugin @Inject lateinit var openAPSSMBDynamicISFPlugin: OpenAPSSMBDynamicISFPlugin @@ -212,6 +214,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang addPreferencesFromResourceIfEnabled(virtualPumpPlugin, rootKey) addPreferencesFromResourceIfEnabled(insulinOrefFreePeakPlugin, rootKey) addPreferencesFromResourceIfEnabled(nsClientPlugin, rootKey) + addPreferencesFromResourceIfEnabled(nsClientV3Plugin, rootKey) addPreferencesFromResourceIfEnabled(tidepoolPlugin, rootKey) addPreferencesFromResourceIfEnabled(smsCommunicatorPlugin, rootKey) addPreferencesFromResourceIfEnabled(automationPlugin, rootKey) @@ -328,13 +331,11 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang var visible = false if (p is PreferenceGroup) { - for (i in 0 until p.preferenceCount) { + for (i in 0 until p.preferenceCount) visible = updateFilterVisibility(filter, p.getPreference(i)) || visible - } - if (visible && p is PreferenceCategory) { - p.initialExpandedChildrenCount = Int.MAX_VALUE - } + if (visible && p is PreferenceCategory) p.initialExpandedChildrenCount = Int.MAX_VALUE } else { + @Suppress("KotlinConstantConditions") visible = visible || p.key?.contains(filter, true) == true visible = visible || p.title?.contains(filter, true) == true visible = visible || p.summary?.contains(filter, true) == true diff --git a/app/src/main/java/info/nightscout/androidaps/di/AppModule.kt b/app/src/main/java/info/nightscout/androidaps/di/AppModule.kt index 35789d6948..df429cd091 100644 --- a/app/src/main/java/info/nightscout/androidaps/di/AppModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/di/AppModule.kt @@ -43,10 +43,10 @@ import info.nightscout.androidaps.plugins.configBuilder.PluginStore import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctionImpl import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefsImpl import info.nightscout.androidaps.plugins.general.maintenance.PrefFileListProvider -import info.nightscout.androidaps.plugins.general.nsclient.DataSyncSelectorImplementation -import info.nightscout.androidaps.plugins.general.nsclient.data.DeviceStatusData import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.plugins.pump.PumpSyncImplementation +import info.nightscout.androidaps.plugins.sync.nsclient.DataSyncSelectorImplementation +import info.nightscout.androidaps.plugins.sync.nsclient.data.ProcessedDeviceStatusData import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.HardLimits @@ -115,11 +115,11 @@ open class AppModule { aapsLogger: AAPSLogger, sp: SP, rxBus: RxBus, rh: ResourceHelper, activePlugin: ActivePlugin, repository: AppRepository, dateUtil: DateUtil, config: Config, hardLimits: HardLimits, - aapsSchedulers: AapsSchedulers, fabricPrivacy: FabricPrivacy, deviceStatusData: DeviceStatusData + aapsSchedulers: AapsSchedulers, fabricPrivacy: FabricPrivacy, processedDeviceStatusData: ProcessedDeviceStatusData ): ProfileFunction = ProfileFunctionImpl( aapsLogger, sp, rxBus, rh, activePlugin, repository, dateUtil, - config, hardLimits, aapsSchedulers, fabricPrivacy, deviceStatusData + config, hardLimits, aapsSchedulers, fabricPrivacy, processedDeviceStatusData ) @Provides diff --git a/app/src/main/java/info/nightscout/androidaps/di/FragmentsModule.kt b/app/src/main/java/info/nightscout/androidaps/di/FragmentsModule.kt index 756d619ac5..5765478431 100644 --- a/app/src/main/java/info/nightscout/androidaps/di/FragmentsModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/di/FragmentsModule.kt @@ -20,13 +20,13 @@ import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesFragm import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog import info.nightscout.androidaps.plugins.general.actions.ActionsFragment import info.nightscout.androidaps.plugins.general.maintenance.MaintenanceFragment -import info.nightscout.androidaps.plugins.general.nsclient.NSClientFragment import info.nightscout.androidaps.plugins.general.overview.OverviewFragment import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWizardDialog -import info.nightscout.androidaps.plugins.general.tidepool.TidepoolFragment import info.nightscout.androidaps.plugins.general.wear.WearFragment import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpFragment import info.nightscout.androidaps.plugins.source.BGSourceFragment +import info.nightscout.androidaps.plugins.sync.nsShared.NSClientFragment +import info.nightscout.androidaps.plugins.sync.tidepool.TidepoolFragment import info.nightscout.androidaps.utils.protection.PasswordCheck import info.nightscout.plugins.general.autotune.AutotuneFragment diff --git a/app/src/main/java/info/nightscout/androidaps/di/PluginsListModule.kt b/app/src/main/java/info/nightscout/androidaps/di/PluginsListModule.kt index 81af39bf17..a8eecb1e3d 100644 --- a/app/src/main/java/info/nightscout/androidaps/di/PluginsListModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/di/PluginsListModule.kt @@ -21,10 +21,8 @@ import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin import info.nightscout.androidaps.plugins.general.actions.ActionsPlugin import info.nightscout.androidaps.plugins.general.dataBroadcaster.DataBroadcastPlugin import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin -import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin -import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin import info.nightscout.androidaps.plugins.general.wear.WearPlugin import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin @@ -48,6 +46,9 @@ import info.nightscout.androidaps.plugins.source.PoctechPlugin import info.nightscout.androidaps.plugins.source.RandomBgPlugin import info.nightscout.androidaps.plugins.source.TomatoPlugin import info.nightscout.androidaps.plugins.source.XdripPlugin +import info.nightscout.androidaps.plugins.sync.nsclient.NSClientPlugin +import info.nightscout.androidaps.plugins.sync.nsclientV3.NSClientV3Plugin +import info.nightscout.androidaps.plugins.sync.tidepool.TidepoolPlugin import info.nightscout.automation.AutomationPlugin import info.nightscout.plugins.constraints.bgQualityCheck.BgQualityCheckPlugin import info.nightscout.plugins.constraints.dstHelper.DstHelperPlugin @@ -316,6 +317,12 @@ abstract class PluginsListModule { @IntKey(368) abstract fun bindTidepoolPlugin(plugin: TidepoolPlugin): PluginBase + @Binds + @Unfinished + @IntoMap + @IntKey(362) + abstract fun bindNSClientV3Plugin(plugin: NSClientV3Plugin): PluginBase + @Binds @AllConfigs @IntoMap diff --git a/app/src/main/java/info/nightscout/androidaps/di/ServicesModule.kt b/app/src/main/java/info/nightscout/androidaps/di/ServicesModule.kt index 83dcefc853..dab1c764e2 100644 --- a/app/src/main/java/info/nightscout/androidaps/di/ServicesModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/di/ServicesModule.kt @@ -2,7 +2,7 @@ package info.nightscout.androidaps.di import dagger.Module import dagger.android.ContributesAndroidInjector -import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService +import info.nightscout.androidaps.plugins.sync.nsclient.services.NSClientService import info.nightscout.androidaps.plugins.general.overview.notifications.DismissNotificationService import info.nightscout.androidaps.plugins.general.persistentNotification.DummyService import info.nightscout.androidaps.plugins.general.wear.wearintegration.DataLayerListenerServiceMobile diff --git a/app/src/main/java/info/nightscout/androidaps/di/WorkersModule.kt b/app/src/main/java/info/nightscout/androidaps/di/WorkersModule.kt index a4cf47a767..ad9b25c340 100644 --- a/app/src/main/java/info/nightscout/androidaps/di/WorkersModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/di/WorkersModule.kt @@ -3,11 +3,6 @@ package info.nightscout.androidaps.di import dagger.Module import dagger.android.ContributesAndroidInjector import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefsImpl -import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddAckWorker -import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddUpdateWorker -import info.nightscout.androidaps.plugins.general.nsclient.NSClientMbgWorker -import info.nightscout.androidaps.plugins.general.nsclient.NSClientUpdateRemoveAckWorker -import info.nightscout.plugins.profile.ProfilePlugin import info.nightscout.androidaps.plugins.source.AidexPlugin import info.nightscout.androidaps.plugins.source.DexcomPlugin import info.nightscout.androidaps.plugins.source.EversensePlugin @@ -17,6 +12,18 @@ import info.nightscout.androidaps.plugins.source.NSClientSourcePlugin import info.nightscout.androidaps.plugins.source.PoctechPlugin import info.nightscout.androidaps.plugins.source.TomatoPlugin import info.nightscout.androidaps.plugins.source.XdripPlugin +import info.nightscout.androidaps.plugins.sync.nsShared.StoreDataForDb +import info.nightscout.androidaps.plugins.sync.nsclient.NSClientAddAckWorker +import info.nightscout.androidaps.plugins.sync.nsclient.NSClientAddUpdateWorker +import info.nightscout.androidaps.plugins.sync.nsclient.NSClientMbgWorker +import info.nightscout.androidaps.plugins.sync.nsclient.NSClientUpdateRemoveAckWorker +import info.nightscout.androidaps.plugins.sync.nsclientV3.workers.LoadBgWorker +import info.nightscout.androidaps.plugins.sync.nsclientV3.workers.LoadDeviceStatusWorker +import info.nightscout.androidaps.plugins.sync.nsclientV3.workers.LoadLastModificationWorker +import info.nightscout.androidaps.plugins.sync.nsclientV3.workers.LoadStatusWorker +import info.nightscout.androidaps.plugins.sync.nsclientV3.workers.LoadTreatmentsWorker +import info.nightscout.androidaps.plugins.sync.nsclientV3.workers.ProcessTreatmentsWorker +import info.nightscout.plugins.profile.ProfilePlugin @Module @Suppress("unused") @@ -37,4 +44,11 @@ abstract class WorkersModule { @ContributesAndroidInjector abstract fun contributesNSClientMbgWorker(): NSClientMbgWorker @ContributesAndroidInjector abstract fun contributesCsvExportWorker(): ImportExportPrefsImpl.CsvExportWorker @ContributesAndroidInjector abstract fun contributesAidexWorker(): AidexPlugin.AidexWorker + @ContributesAndroidInjector abstract fun contributesLoadStatusWorker(): LoadStatusWorker + @ContributesAndroidInjector abstract fun contributesLoadLastModificationWorker(): LoadLastModificationWorker + @ContributesAndroidInjector abstract fun contributesLoadBgWorker(): LoadBgWorker + @ContributesAndroidInjector abstract fun contributesStoreBgWorker(): StoreDataForDb.StoreBgWorker + @ContributesAndroidInjector abstract fun contributesTreatmentWorker(): LoadTreatmentsWorker + @ContributesAndroidInjector abstract fun contributesProcessTreatmentsWorker(): ProcessTreatmentsWorker + @ContributesAndroidInjector abstract fun contributesLoadDeviceStatusWorker(): LoadDeviceStatusWorker } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt index 271bb0a073..5a34e252df 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt @@ -13,7 +13,6 @@ import android.os.SystemClock import androidx.core.app.NotificationCompat import dagger.android.HasAndroidInjector import info.nightscout.androidaps.BuildConfig -import info.nightscout.interfaces.Constants import info.nightscout.androidaps.MainActivity import info.nightscout.androidaps.R import info.nightscout.androidaps.annotations.OpenForTesting @@ -27,22 +26,17 @@ import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction -import info.nightscout.androidaps.extensions.buildDeviceStatus import info.nightscout.androidaps.extensions.convertedToAbsolute import info.nightscout.androidaps.extensions.convertedToPercent import info.nightscout.androidaps.extensions.plannedRemainingMinutes import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.ActivityNames import info.nightscout.androidaps.interfaces.CommandQueue -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraints import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.Loop import info.nightscout.androidaps.interfaces.Loop.LastRun import info.nightscout.androidaps.interfaces.PluginBase -import info.nightscout.interfaces.PluginDescription -import info.nightscout.interfaces.PluginType import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.PumpDescription @@ -55,14 +49,20 @@ import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotifi import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification -import info.nightscout.interfaces.notifications.Notification import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin -import info.nightscout.interfaces.queue.Callback import info.nightscout.androidaps.receivers.ReceiverStatusStore import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.extensions.buildDeviceStatus +import info.nightscout.interfaces.ActivityNames +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.Constants +import info.nightscout.interfaces.PluginDescription +import info.nightscout.interfaces.PluginType +import info.nightscout.interfaces.notifications.Notification +import info.nightscout.interfaces.queue.Callback import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventAcceptOpenLoopChange diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt index 9fe186b0a7..14797d64db 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt @@ -100,7 +100,7 @@ class OpenAPSAMAPlugin @Inject constructor( aapsLogger.debug(LTag.APS, rh.gs(R.string.noprofileset)) return } - if (!isEnabled(PluginType.APS)) { + if (!isEnabled()) { rxBus.send(EventOpenAPSUpdateResultGui(rh.gs(R.string.openapsma_disabled))) aapsLogger.debug(LTag.APS, rh.gs(R.string.openapsma_disabled)) return diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt index dec26786ee..5d70de8377 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt @@ -109,7 +109,7 @@ class OpenAPSSMBPlugin @Inject constructor( aapsLogger.debug(LTag.APS, rh.gs(R.string.noprofileset)) return } - if (!isEnabled(PluginType.APS)) { + if (!isEnabled()) { rxBus.send(EventOpenAPSUpdateResultGui(rh.gs(R.string.openapsma_disabled))) aapsLogger.debug(LTag.APS, rh.gs(R.string.openapsma_disabled)) return diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt index e68d37f28a..ba1cd689fe 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt @@ -114,6 +114,7 @@ class ConfigBuilderFragment : DaggerFragment() { createViewsForPlugins(R.string.configbuilder_loop, R.string.configbuilder_loop_description, PluginType.LOOP, activePlugin.getSpecificPluginsVisibleInList(PluginType.LOOP)) createViewsForPlugins(R.string.constraints, R.string.configbuilder_constraints_description, PluginType.CONSTRAINTS, activePlugin.getSpecificPluginsVisibleInList(PluginType.CONSTRAINTS)) } + createViewsForPlugins(R.string.configbuilder_sync, R.string.configbuilder_sync_description, PluginType.SYNC, activePlugin.getSpecificPluginsVisibleInList(PluginType.SYNC)) createViewsForPlugins(R.string.configbuilder_general, R.string.configbuilder_general_description, PluginType.GENERAL, activePlugin.getSpecificPluginsVisibleInList(PluginType.GENERAL)) } @@ -208,7 +209,7 @@ class ConfigBuilderFragment : DaggerFragment() { } private fun areMultipleSelectionsAllowed(type: PluginType): Boolean { - return type == PluginType.GENERAL || type == PluginType.CONSTRAINTS || type == PluginType.LOOP + return type == PluginType.GENERAL || type == PluginType.CONSTRAINTS || type == PluginType.LOOP || type == PluginType.SYNC } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.kt index 2cceb145b4..c22ac4fbc5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.kt @@ -11,9 +11,8 @@ import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.BgSource import info.nightscout.androidaps.interfaces.ConfigBuilder import info.nightscout.androidaps.interfaces.Insulin +import info.nightscout.androidaps.interfaces.NsClient import info.nightscout.androidaps.interfaces.PluginBase -import info.nightscout.interfaces.PluginDescription -import info.nightscout.interfaces.PluginType import info.nightscout.androidaps.interfaces.ProfileSource import info.nightscout.androidaps.interfaces.Pump import info.nightscout.androidaps.interfaces.PumpSync @@ -22,6 +21,8 @@ import info.nightscout.androidaps.interfaces.Sensitivity import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui import info.nightscout.androidaps.utils.alertDialogs.OKDialog +import info.nightscout.interfaces.PluginDescription +import info.nightscout.interfaces.PluginType import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventAppInitialized import info.nightscout.rx.events.EventConfigBuilderChange @@ -44,15 +45,15 @@ class ConfigBuilderPlugin @Inject constructor( private val pumpSync: PumpSync ) : PluginBase( PluginDescription() - .mainType(PluginType.GENERAL) - .fragmentClass(ConfigBuilderFragment::class.java.name) - .showInList(true) - .alwaysEnabled(true) - .alwaysVisible(false) - .pluginIcon(R.drawable.ic_cogs) - .pluginName(R.string.configbuilder) - .shortName(R.string.configbuilder_shortname) - .description(R.string.description_config_builder), + .mainType(PluginType.GENERAL) + .fragmentClass(ConfigBuilderFragment::class.java.name) + .showInList(true) + .alwaysEnabled(true) + .alwaysVisible(false) + .pluginIcon(R.drawable.ic_cogs) + .pluginName(R.string.configbuilder) + .shortName(R.string.configbuilder_shortname) + .description(R.string.description_config_builder), aapsLogger, rh, injector ), ConfigBuilder { @@ -77,58 +78,60 @@ class ConfigBuilderPlugin @Inject constructor( val type = p.getType() if (p.pluginDescription.alwaysEnabled && p.pluginDescription.alwaysVisible) continue if (p.pluginDescription.alwaysEnabled && p.pluginDescription.neverVisible) continue - savePref(p, type, true) + savePref(p, type) } } - private fun savePref(p: PluginBase, type: PluginType, storeVisible: Boolean) { + private fun savePref(p: PluginBase, type: PluginType) { val settingEnabled = "ConfigBuilder_" + type.name + "_" + p.javaClass.simpleName + "_Enabled" sp.putBoolean(settingEnabled, p.isEnabled()) aapsLogger.debug(LTag.CONFIGBUILDER, "Storing: " + settingEnabled + ":" + p.isEnabled()) - if (storeVisible) { - val settingVisible = "ConfigBuilder_" + type.name + "_" + p.javaClass.simpleName + "_Visible" - sp.putBoolean(settingVisible, p.isFragmentVisible()) - aapsLogger.debug(LTag.CONFIGBUILDER, "Storing: " + settingVisible + ":" + p.isFragmentVisible()) - } + val settingVisible = "ConfigBuilder_" + type.name + "_" + p.javaClass.simpleName + "_Visible" + sp.putBoolean(settingVisible, p.isFragmentVisible()) + aapsLogger.debug(LTag.CONFIGBUILDER, "Storing: " + settingVisible + ":" + p.isFragmentVisible()) } private fun loadSettings() { aapsLogger.debug(LTag.CONFIGBUILDER, "Loading stored settings") for (p in activePlugin.getPluginsList()) { val type = p.getType() - loadPref(p, type, true) + loadPref(p, type) } activePlugin.verifySelectionInCategories() } - private fun loadPref(p: PluginBase, type: PluginType, loadVisible: Boolean) { + private fun loadPref(p: PluginBase, type: PluginType) { val settingEnabled = "ConfigBuilder_" + type.name + "_" + p.javaClass.simpleName + "_Enabled" - if (sp.contains(settingEnabled)) p.setPluginEnabled(type, sp.getBoolean(settingEnabled, false)) else if (p.getType() == type && (p.pluginDescription.enableByDefault || p.pluginDescription.alwaysEnabled)) { + if (sp.contains(settingEnabled)) p.setPluginEnabled( + type, + sp.getBoolean(settingEnabled, false) + ) else if (p.getType() == type && (p.pluginDescription.enableByDefault || p.pluginDescription.alwaysEnabled)) { p.setPluginEnabled(type, true) } aapsLogger.debug(LTag.CONFIGBUILDER, "Loaded: " + settingEnabled + ":" + p.isEnabled(type)) - if (loadVisible) { - val settingVisible = "ConfigBuilder_" + type.name + "_" + p.javaClass.simpleName + "_Visible" - if (sp.contains(settingVisible)) p.setFragmentVisible(type, sp.getBoolean(settingVisible, false) && sp.getBoolean(settingEnabled, false)) else if (p.getType() == type && p.pluginDescription.visibleByDefault) { - p.setFragmentVisible(type, true) - } - aapsLogger.debug(LTag.CONFIGBUILDER, "Loaded: " + settingVisible + ":" + p.isFragmentVisible()) + val settingVisible = "ConfigBuilder_" + type.name + "_" + p.javaClass.simpleName + "_Visible" + if (sp.contains(settingVisible)) p.setFragmentVisible( + type, + sp.getBoolean(settingVisible, false) && sp.getBoolean(settingEnabled, false) + ) else if (p.getType() == type && p.pluginDescription.visibleByDefault) { + p.setFragmentVisible(type, true) } + aapsLogger.debug(LTag.CONFIGBUILDER, "Loaded: " + settingVisible + ":" + p.isFragmentVisible()) } fun logPluginStatus() { for (p in activePlugin.getPluginsList()) { aapsLogger.debug( LTag.CONFIGBUILDER, p.name + ":" + - (if (p.isEnabled(PluginType.GENERAL)) " GENERAL" else "") + - (if (p.isEnabled(PluginType.SENSITIVITY)) " SENSITIVITY" else "") + - (if (p.isEnabled(PluginType.PROFILE)) " PROFILE" else "") + - (if (p.isEnabled(PluginType.APS)) " APS" else "") + - (if (p.isEnabled(PluginType.PUMP)) " PUMP" else "") + - (if (p.isEnabled(PluginType.CONSTRAINTS)) " CONSTRAINTS" else "") + - (if (p.isEnabled(PluginType.LOOP)) " LOOP" else "") + - (if (p.isEnabled(PluginType.BGSOURCE)) " BGSOURCE" else "") + - if (p.isEnabled(PluginType.INSULIN)) " INSULIN" else "" + (if (p.isEnabled(PluginType.GENERAL)) " GENERAL" else "") + + (if (p.isEnabled(PluginType.SENSITIVITY)) " SENSITIVITY" else "") + + (if (p.isEnabled(PluginType.PROFILE)) " PROFILE" else "") + + (if (p.isEnabled(PluginType.APS)) " APS" else "") + + (if (p.isEnabled(PluginType.PUMP)) " PUMP" else "") + + (if (p.isEnabled(PluginType.CONSTRAINTS)) " CONSTRAINTS" else "") + + (if (p.isEnabled(PluginType.LOOP)) " LOOP" else "") + + (if (p.isEnabled(PluginType.BGSOURCE)) " BGSOURCE" else "") + + if (p.isEnabled(PluginType.INSULIN)) " INSULIN" else "" ) } } @@ -153,36 +156,41 @@ class ConfigBuilderPlugin @Inject constructor( performPluginSwitch(changedPlugin, newState, type) pumpSync.connectNewPump() sp.putBoolean("allow_hardware_pump", true) - uel.log(Action.HW_PUMP_ALLOWED, Sources.ConfigBuilder, rh.gs(changedPlugin.pluginDescription.pluginName), - ValueWithUnit.SimpleString(rh.gsNotLocalised(changedPlugin.pluginDescription.pluginName))) + uel.log( + Action.HW_PUMP_ALLOWED, Sources.ConfigBuilder, rh.gs(changedPlugin.pluginDescription.pluginName), + ValueWithUnit.SimpleString(rh.gsNotLocalised(changedPlugin.pluginDescription.pluginName)) + ) aapsLogger.debug(LTag.PUMP, "First time HW pump allowed!") }, { - rxBus.send(EventConfigBuilderUpdateGui()) - aapsLogger.debug(LTag.PUMP, "User does not allow switching to HW pump!") - }) + rxBus.send(EventConfigBuilderUpdateGui()) + aapsLogger.debug(LTag.PUMP, "User does not allow switching to HW pump!") + }) } } override fun performPluginSwitch(changedPlugin: PluginBase, enabled: Boolean, type: PluginType) { - if(enabled && !changedPlugin.isEnabled()) { - uel.log(Action.PLUGIN_ENABLED, Sources.ConfigBuilder, rh.gs(changedPlugin.pluginDescription.pluginName), - ValueWithUnit.SimpleString(rh.gsNotLocalised(changedPlugin.pluginDescription.pluginName))) - } - else if(!enabled) { - uel.log(Action.PLUGIN_DISABLED, Sources.ConfigBuilder, rh.gs(changedPlugin.pluginDescription.pluginName), - ValueWithUnit.SimpleString(rh.gsNotLocalised(changedPlugin.pluginDescription.pluginName))) + if (enabled && !changedPlugin.isEnabled()) { + uel.log( + Action.PLUGIN_ENABLED, Sources.ConfigBuilder, rh.gs(changedPlugin.pluginDescription.pluginName), + ValueWithUnit.SimpleString(rh.gsNotLocalised(changedPlugin.pluginDescription.pluginName)) + ) + } else if (!enabled) { + uel.log( + Action.PLUGIN_DISABLED, Sources.ConfigBuilder, rh.gs(changedPlugin.pluginDescription.pluginName), + ValueWithUnit.SimpleString(rh.gsNotLocalised(changedPlugin.pluginDescription.pluginName)) + ) } changedPlugin.setPluginEnabled(type, enabled) changedPlugin.setFragmentVisible(type, enabled) processOnEnabledCategoryChanged(changedPlugin, type) - storeSettings("CheckedCheckboxEnabled") + storeSettings("RemoteConfiguration") rxBus.send(EventRebuildTabs()) rxBus.send(EventConfigBuilderChange()) rxBus.send(EventConfigBuilderUpdateGui()) logPluginStatus() } - fun processOnEnabledCategoryChanged(changedPlugin: PluginBase, type: PluginType?) { + fun processOnEnabledCategoryChanged(changedPlugin: PluginBase, type: PluginType) { var pluginsInCategory: ArrayList? = null when (type) { PluginType.INSULIN -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Insulin::class.java) @@ -191,12 +199,14 @@ class ConfigBuilderPlugin @Inject constructor( PluginType.PROFILE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(ProfileSource::class.java) PluginType.BGSOURCE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(BgSource::class.java) PluginType.PUMP -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Pump::class.java) + // Process only NSClients + PluginType.SYNC -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(NsClient::class.java) else -> { } } if (pluginsInCategory != null) { - val newSelection = changedPlugin.isEnabled(type!!) + val newSelection = changedPlugin.isEnabled(type) if (newSelection) { // new plugin selected -> disable others for (p in pluginsInCategory) { if (p.name == changedPlugin.name) { @@ -206,7 +216,9 @@ class ConfigBuilderPlugin @Inject constructor( p.setFragmentVisible(type, false) } } - } else { // enable first plugin in list + } else if (type != PluginType.SYNC) { + // enable first plugin in list + // NSC must not be selected pluginsInCategory[0].setPluginEnabled(type, true) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt index d00bd1f231..a800d7e8b1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt @@ -3,16 +3,18 @@ package info.nightscout.androidaps.plugins.configBuilder import info.nightscout.androidaps.interfaces.APS import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.BgSource -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.Insulin import info.nightscout.androidaps.interfaces.IobCobCalculator +import info.nightscout.androidaps.interfaces.NsClient import info.nightscout.androidaps.interfaces.Overview import info.nightscout.androidaps.interfaces.PluginBase -import info.nightscout.interfaces.PluginType import info.nightscout.androidaps.interfaces.ProfileSource import info.nightscout.androidaps.interfaces.Pump -import info.nightscout.interfaces.Safety import info.nightscout.androidaps.interfaces.Sensitivity +import info.nightscout.androidaps.interfaces.Sync +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.PluginType +import info.nightscout.interfaces.Safety import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag import javax.inject.Inject @@ -133,8 +135,9 @@ class PluginStore @Inject constructor( setFragmentVisibilities((activePumpStore as PluginBase).name, pluginsInCategory, PluginType.PUMP) } - private fun setFragmentVisibilities(activePluginName: String, pluginsInCategory: ArrayList, - pluginType: PluginType + private fun setFragmentVisibilities( + activePluginName: String, pluginsInCategory: ArrayList, + pluginType: PluginType ) { aapsLogger.debug(LTag.CONFIGBUILDER, "Selected interface: $activePluginName") for (p in pluginsInCategory) @@ -185,6 +188,17 @@ class PluginStore @Inject constructor( override val activeIobCobCalculator: IobCobCalculator get() = getSpecificPluginsListByInterface(IobCobCalculator::class.java).first() as IobCobCalculator + override val activeNsClient: NsClient? + get() = getTheOneEnabledInArray(getSpecificPluginsListByInterface(NsClient::class.java), PluginType.SYNC) as NsClient? + + @Suppress("UNCHECKED_CAST") + override val firstActiveSync: Sync? + get() = (getSpecificPluginsList(PluginType.SYNC) as ArrayList).firstOrNull { it.connected } + + @Suppress("UNCHECKED_CAST") + override val activeSyncs: ArrayList + get() = getSpecificPluginsList(PluginType.SYNC) as ArrayList + override fun getPluginsList(): ArrayList = ArrayList(plugins) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctionImpl.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctionImpl.kt index f00593b60c..cd028221c4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctionImpl.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctionImpl.kt @@ -1,6 +1,5 @@ package info.nightscout.androidaps.plugins.configBuilder -import info.nightscout.interfaces.Constants import info.nightscout.androidaps.core.R import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.database.AppRepository @@ -8,19 +7,20 @@ import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.entities.ProfileSwitch import info.nightscout.androidaps.database.transactions.InsertOrUpdateProfileSwitch import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged -import info.nightscout.androidaps.extensions.fromConstant import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileStore import info.nightscout.androidaps.interfaces.ResourceHelper -import info.nightscout.androidaps.plugins.general.nsclient.data.DeviceStatusData +import info.nightscout.androidaps.plugins.sync.nsclient.data.ProcessedDeviceStatusData import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.extensions.fromConstant +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.Constants import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus import info.nightscout.rx.logging.AAPSLogger @@ -45,7 +45,7 @@ class ProfileFunctionImpl @Inject constructor( private val hardLimits: HardLimits, aapsSchedulers: AapsSchedulers, private val fabricPrivacy: FabricPrivacy, - private val deviceStatusData: DeviceStatusData + private val processedDeviceStatusData: ProcessedDeviceStatusData ) : ProfileFunction { private var cache = ConcurrentHashMap() @@ -117,7 +117,7 @@ class ProfileFunctionImpl @Inject constructor( // Try to get it from device status // Remove this code after switch to api v3 if (config.NSCLIENT && ps is ValueWrapper.Absent) { - deviceStatusData.pumpData?.activeProfileName?.let { activeProfile -> + processedDeviceStatusData.pumpData?.activeProfileName?.let { activeProfile -> activePlugin.activeProfileSource.profile?.getSpecificProfile(activeProfile)?.let { ap -> val sealed = ProfileSealed.Pure(ap) synchronized(cache) { @@ -183,7 +183,7 @@ class ProfileFunctionImpl @Inject constructor( val profileStore = activePlugin.activeProfileSource.profile ?: return false val ps = buildProfileSwitch(profileStore, profile.profileName, durationInMinutes, percentage, 0, dateUtil.now()) ?: return false val validity = ProfileSealed.PS(ps).isValid( - rh.gs(info.nightscout.automation.R.string.careportal_profileswitch), + rh.gs(R.string.careportal_profileswitch), activePlugin.activePump, config, rh, diff --git a/core/src/main/java/info/nightscout/androidaps/plugins/configBuilder/RunningConfiguration.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/RunningConfiguration.kt similarity index 74% rename from core/src/main/java/info/nightscout/androidaps/plugins/configBuilder/RunningConfiguration.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/RunningConfiguration.kt index e48d0434ee..4d1db04996 100644 --- a/core/src/main/java/info/nightscout/androidaps/plugins/configBuilder/RunningConfiguration.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/RunningConfiguration.kt @@ -2,21 +2,22 @@ package info.nightscout.androidaps.plugins.configBuilder import info.nightscout.androidaps.core.R import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.ConfigBuilder import info.nightscout.androidaps.interfaces.Insulin -import info.nightscout.interfaces.PluginType +import info.nightscout.androidaps.interfaces.NsClient import info.nightscout.androidaps.interfaces.PumpSync import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.interfaces.Sensitivity -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification -import info.nightscout.interfaces.notifications.Notification import info.nightscout.androidaps.plugins.pump.common.defs.PumpType -import info.nightscout.interfaces.utils.JsonHelper +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientNewLog +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.PluginType +import info.nightscout.interfaces.notifications.Notification import info.nightscout.rx.bus.RxBus import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag +import info.nightscout.sdk.remotemodel.RemoteDeviceStatus import info.nightscout.shared.sharedPreferences.SP import org.json.JSONException import org.json.JSONObject @@ -66,17 +67,16 @@ class RunningConfiguration @Inject constructor( } // called in NSClient mode only - fun apply(configuration: JSONObject) { + fun apply(configuration: RemoteDeviceStatus.Configuration, version: NsClient.Version) { assert(config.NSCLIENT) - if (configuration.has("version")) { - rxBus.send(EventNSClientNewLog("VERSION", "Received AndroidAPS version ${configuration.getString("version")}")) - if (config.VERSION_NAME.startsWith(configuration.getString("version")).not()) { + configuration.version?.let { + rxBus.send(EventNSClientNewLog("VERSION", "Received AndroidAPS version $it", version)) + if (config.VERSION_NAME.startsWith(it).not()) rxBus.send(EventNewNotification(Notification(Notification.NSCLIENT_VERSION_DOES_NOT_MATCH, rh.gs(R.string.nsclient_version_does_not_match), Notification.NORMAL))) - } } - if (configuration.has("insulin")) { - val insulin = Insulin.InsulinType.fromInt(JsonHelper.safeGetInt(configuration, "insulin", Insulin.InsulinType.UNKNOWN.value)) + configuration.insulin?.let { + val insulin = Insulin.InsulinType.fromInt(it) for (p in activePlugin.getSpecificPluginsListByInterface(Insulin::class.java)) { val insulinPlugin = p as Insulin if (insulinPlugin.id == insulin) { @@ -84,13 +84,13 @@ class RunningConfiguration @Inject constructor( aapsLogger.debug(LTag.CORE, "Changing insulin plugin to ${insulin.name}") configBuilder.performPluginSwitch(p, true, PluginType.INSULIN) } - insulinPlugin.applyConfiguration(configuration.getJSONObject("insulinConfiguration")) + configuration.insulinConfiguration?.let { ic -> insulinPlugin.applyConfiguration(ic) } } } } - if (configuration.has("sensitivity")) { - val sensitivity = Sensitivity.SensitivityType.fromInt(JsonHelper.safeGetInt(configuration, "sensitivity", Sensitivity.SensitivityType.UNKNOWN.value)) + configuration.sensitivity?.let { + val sensitivity = Sensitivity.SensitivityType.fromInt(it) for (p in activePlugin.getSpecificPluginsListByInterface(Sensitivity::class.java)) { val sensitivityPlugin = p as Sensitivity if (sensitivityPlugin.id == sensitivity) { @@ -98,25 +98,26 @@ class RunningConfiguration @Inject constructor( aapsLogger.debug(LTag.CORE, "Changing sensitivity plugin to ${sensitivity.name}") configBuilder.performPluginSwitch(p, true, PluginType.SENSITIVITY) } - sensitivityPlugin.applyConfiguration(configuration.getJSONObject("sensitivityConfiguration")) + configuration.sensitivityConfiguration?.let { sc -> sensitivityPlugin.applyConfiguration(sc) } } } } - if (configuration.has("pump")) { - val pumpType = JsonHelper.safeGetString(configuration, "pump", PumpType.GENERIC_AAPS.description) - if (sp.getString(R.string.key_virtualpump_type, "fake") != pumpType) { - sp.putString(R.string.key_virtualpump_type, pumpType) - activePlugin.activePump.pumpDescription.fillFor(PumpType.getByDescription(pumpType)) + configuration.pump?.let { + if (sp.getString(R.string.key_virtualpump_type, "fake") != it) { + sp.putString(R.string.key_virtualpump_type, it) + activePlugin.activePump.pumpDescription.fillFor(PumpType.getByDescription(it)) pumpSync.connectNewPump(endRunning = false) // do not end running TBRs, we call this only to accept data properly - aapsLogger.debug(LTag.CORE, "Changing pump type to $pumpType") + aapsLogger.debug(LTag.CORE, "Changing pump type to $it") } } - if (configuration.has("overviewConfiguration")) - activePlugin.activeOverview.applyConfiguration(configuration.getJSONObject("overviewConfiguration")) + configuration.overviewConfiguration?.let { + activePlugin.activeOverview.applyConfiguration(it) + } - if (configuration.has("safetyConfiguration")) - activePlugin.activeSafety.applyConfiguration(configuration.getJSONObject("safetyConfiguration")) + configuration.safetyConfiguration?.let { + activePlugin.activeSafety.applyConfiguration(it) + } } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt index 32db320167..94e02b782f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt @@ -1,22 +1,12 @@ package info.nightscout.androidaps.plugins.constraints.objectives -import androidx.fragment.app.FragmentActivity -import com.google.common.base.Charsets -import com.google.common.hash.Hashing import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.BuildConfig import info.nightscout.androidaps.R -import info.nightscout.androidaps.database.entities.UserEntry.Action -import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraints import info.nightscout.androidaps.interfaces.PluginBase -import info.nightscout.interfaces.PluginDescription -import info.nightscout.interfaces.PluginType import info.nightscout.androidaps.interfaces.ResourceHelper -import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective0 import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective1 @@ -28,11 +18,11 @@ import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Obje import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective6 import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective7 import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective9 -import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.alertDialogs.OKDialog +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.PluginDescription +import info.nightscout.interfaces.PluginType import info.nightscout.rx.logging.AAPSLogger import info.nightscout.shared.sharedPreferences.SP -import java.util.Locale import javax.inject.Inject import javax.inject.Singleton @@ -43,19 +33,17 @@ class ObjectivesPlugin @Inject constructor( rh: ResourceHelper, private val activePlugin: ActivePlugin, private val sp: SP, - config: Config, - private val dateUtil: DateUtil, - private val uel: UserEntryLogger + config: Config ) : PluginBase( PluginDescription() - .mainType(PluginType.CONSTRAINTS) - .fragmentClass(ObjectivesFragment::class.qualifiedName) - .alwaysEnabled(config.APS) - .showInList(config.APS) - .pluginIcon(R.drawable.ic_graduation) - .pluginName(R.string.objectives) - .shortName(R.string.objectives_shortname) - .description(R.string.description_objectives), + .mainType(PluginType.CONSTRAINTS) + .fragmentClass(ObjectivesFragment::class.qualifiedName) + .alwaysEnabled(config.APS) + .showInList(config.APS) + .pluginIcon(R.drawable.ic_graduation) + .pluginName(R.string.objectives) + .shortName(R.string.objectives_shortname) + .description(R.string.description_objectives), aapsLogger, rh, injector ), Constraints { @@ -115,34 +103,6 @@ class ObjectivesPlugin @Inject constructor( sp.putBoolean(R.string.key_objectiveusescale, false) } - fun completeObjectives(activity: FragmentActivity, request: String) { - val requestCode = sp.getString(R.string.key_objectives_request_code, "") - var url = sp.getString(R.string.key_nsclientinternal_url, "").lowercase(Locale.getDefault()) - if (!url.endsWith("/")) url = "$url/" - @Suppress("DEPRECATION", "UnstableApiUsage") val hashNS = Hashing.sha1().hashString(url + BuildConfig.APPLICATION_ID + "/" + requestCode, Charsets.UTF_8).toString() - if (request.equals(hashNS.substring(0, 10), ignoreCase = true)) { - sp.putLong("Objectives_" + "openloop" + "_started", dateUtil.now()) - sp.putLong("Objectives_" + "openloop" + "_accomplished", dateUtil.now()) - sp.putLong("Objectives_" + "maxbasal" + "_started", dateUtil.now()) - sp.putLong("Objectives_" + "maxbasal" + "_accomplished", dateUtil.now()) - sp.putLong("Objectives_" + "maxiobzero" + "_started", dateUtil.now()) - sp.putLong("Objectives_" + "maxiobzero" + "_accomplished", dateUtil.now()) - sp.putLong("Objectives_" + "maxiob" + "_started", dateUtil.now()) - sp.putLong("Objectives_" + "maxiob" + "_accomplished", dateUtil.now()) - sp.putLong("Objectives_" + "autosens" + "_started", dateUtil.now()) - sp.putLong("Objectives_" + "autosens" + "_accomplished", dateUtil.now()) - sp.putLong("Objectives_" + "smb" + "_started", dateUtil.now()) - sp.putLong("Objectives_" + "smb" + "_accomplished", dateUtil.now()) - sp.putLong("Objectives_" + "auto" + "_started", dateUtil.now()) - sp.putLong("Objectives_" + "auto" + "_accomplished", dateUtil.now()) - setupObjectives() - OKDialog.show(activity, rh.gs(R.string.objectives), rh.gs(R.string.codeaccepted)) - uel.log(Action.OBJECTIVES_SKIPPED, Sources.Objectives) - } else { - OKDialog.show(activity, rh.gs(R.string.objectives), rh.gs(R.string.codeinvalid)) - } - } - fun allPriorAccomplished(position: Int): Boolean { var accomplished = true for (i in 0 until position) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective0.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective0.kt index bd2eb4d345..9071d4eda3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective0.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective0.kt @@ -8,7 +8,6 @@ import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.Loop import info.nightscout.androidaps.interfaces.PluginBase -import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin import javax.inject.Inject @@ -18,7 +17,6 @@ class Objective0(injector: HasAndroidInjector) : Objective(injector, "config", R @Inject lateinit var virtualPumpPlugin: VirtualPumpPlugin @Inject lateinit var repository: AppRepository @Inject lateinit var loop: Loop - @Inject lateinit var nsClientPlugin: NSClientPlugin @Inject lateinit var iobCobCalculator: IobCobCalculator init { @@ -27,9 +25,9 @@ class Objective0(injector: HasAndroidInjector) : Objective(injector, "config", R return sp.getBoolean(R.string.key_ObjectivesbgIsAvailableInNS, false) } }) - tasks.add(object : Task(this, R.string.nsclienthaswritepermission) { + tasks.add(object : Task(this, R.string.synchaswritepermission) { override fun isCompleted(): Boolean { - return nsClientPlugin.hasWritePermission() + return activePlugin.firstActiveSync?.hasWritePermission == true } }) tasks.add(object : Task(this, R.string.virtualpump_uploadstatus_title) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective3.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective3.kt index 1808666fad..5a89f50ff9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective3.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective3.kt @@ -1,18 +1,15 @@ package info.nightscout.androidaps.plugins.constraints.objectives.objectives -import androidx.fragment.app.FragmentActivity import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R -import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin -import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.utils.T import javax.inject.Inject @Suppress("SpellCheckingInspection") class Objective3 @Inject constructor(injector: HasAndroidInjector) : Objective(injector, "openloop", R.string.objectives_openloop_objective, R.string.objectives_openloop_gate) { - @Inject lateinit var objectivesPlugin: ObjectivesPlugin - @Inject lateinit var nsClientPlugin: NSClientPlugin + @Inject lateinit var activePlugin: ActivePlugin init { tasks.add(MinimumDurationTask(this, T.days(7).msecs())) @@ -22,17 +19,11 @@ class Objective3 @Inject constructor(injector: HasAndroidInjector) : Objective(i } override val progress: String - get() = if (sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED) rh.gs(R.string.completed_well_done) else sp.getInt(R.string.key_ObjectivesmanualEnacts, 0).toString() + " / " + MANUAL_ENACTS_NEEDED + get() = if (sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED) rh.gs(R.string.completed_well_done) else sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) + .toString() + " / " + MANUAL_ENACTS_NEEDED }) } - override fun specialActionEnabled(): Boolean = - nsClientPlugin.nsClientService?.isConnected == true && nsClientPlugin.nsClientService?.hasWriteAuth == true - - override fun specialAction(activity: FragmentActivity, input: String) { - objectivesPlugin.completeObjectives(activity, input) - } - companion object { private const val MANUAL_ENACTS_NEEDED = 20 diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt index 696e688c8a..6a9e7a9307 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt @@ -10,28 +10,27 @@ import info.nightscout.androidaps.extensions.durationInMinutes import info.nightscout.androidaps.extensions.safeQueryBroadcastReceivers import info.nightscout.androidaps.extensions.toStringFull import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.Loop import info.nightscout.androidaps.interfaces.PluginBase -import info.nightscout.interfaces.PluginDescription -import info.nightscout.interfaces.PluginType import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui -import info.nightscout.androidaps.plugins.general.nsclient.data.DeviceStatusData -import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus -import info.nightscout.rx.events.EventOverviewBolusProgress import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider +import info.nightscout.androidaps.plugins.sync.nsclient.data.ProcessedDeviceStatusData import info.nightscout.androidaps.receivers.Intents import info.nightscout.androidaps.receivers.ReceiverStatusStore import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.PluginDescription +import info.nightscout.interfaces.PluginType import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.Event import info.nightscout.rx.events.EventAutosensCalculationFinished +import info.nightscout.rx.events.EventOverviewBolusProgress import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag import io.reactivex.rxjava3.disposables.CompositeDisposable @@ -52,14 +51,12 @@ class DataBroadcastPlugin @Inject constructor( private val iobCobCalculator: IobCobCalculator, private val profileFunction: ProfileFunction, private val defaultValueHelper: DefaultValueHelper, - private val nsDeviceStatus: NSDeviceStatus, - private val deviceStatusData: DeviceStatusData, + private val processedDeviceStatusData: ProcessedDeviceStatusData, private val loop: Loop, private val activePlugin: ActivePlugin, private var receiverStatusStore: ReceiverStatusStore, private val config: Config, private val glucoseStatusProvider: GlucoseStatusProvider - ) : PluginBase( PluginDescription() .mainType(PluginType.GENERAL) @@ -143,7 +140,7 @@ class DataBroadcastPlugin @Inject constructor( private fun loopStatus(bundle: Bundle) { //batteries bundle.putInt("phoneBattery", receiverStatusStore.batteryLevel) - bundle.putInt("rigBattery", nsDeviceStatus.uploaderStatus.replace("%", "").trim { it <= ' ' }.toInt()) + bundle.putInt("rigBattery", processedDeviceStatusData.uploaderStatus.replace("%", "").trim { it <= ' ' }.toInt()) if (config.APS && loop.lastRun?.lastTBREnact != 0L) { //we are AndroidAPS bundle.putLong("suggestedTimeStamp", loop.lastRun?.lastAPSRun ?: -1L) @@ -156,7 +153,7 @@ class DataBroadcastPlugin @Inject constructor( bundle.putString("enacted", loop.lastRun?.request?.json().toString()) } } else { //NSClient or remote - val data = deviceStatusData.openAPSData + val data = processedDeviceStatusData.openAPSData if (data.clockSuggested != 0L && data.suggested != null) { bundle.putLong("suggestedTimeStamp", data.clockSuggested) bundle.putString("suggested", data.suggested.toString()) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenancePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenancePlugin.kt index 2e41632ca9..418624b6d4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenancePlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenancePlugin.kt @@ -13,7 +13,7 @@ import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.interfaces.PluginDescription import info.nightscout.interfaces.PluginType import info.nightscout.androidaps.interfaces.ResourceHelper -import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus +import info.nightscout.androidaps.plugins.sync.nsclient.data.NSSettingsStatus import info.nightscout.plugins.general.maintenance.LoggerUtils import info.nightscout.rx.logging.AAPSLogger import info.nightscout.shared.sharedPreferences.SP diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddUpdateWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddUpdateWorker.kt deleted file mode 100644 index c61ecbc596..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddUpdateWorker.kt +++ /dev/null @@ -1,539 +0,0 @@ -package info.nightscout.androidaps.plugins.general.nsclient - -import android.content.Context -import androidx.work.Worker -import androidx.work.WorkerParameters -import androidx.work.workDataOf -import dagger.android.HasAndroidInjector -import info.nightscout.interfaces.Constants -import info.nightscout.androidaps.R -import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.database.entities.UserEntry.Action -import info.nightscout.androidaps.database.entities.UserEntry.Sources -import info.nightscout.androidaps.database.entities.ValueWithUnit -import info.nightscout.androidaps.database.transactions.SyncNsBolusCalculatorResultTransaction -import info.nightscout.androidaps.database.transactions.SyncNsBolusTransaction -import info.nightscout.androidaps.database.transactions.SyncNsCarbsTransaction -import info.nightscout.androidaps.database.transactions.SyncNsEffectiveProfileSwitchTransaction -import info.nightscout.androidaps.database.transactions.SyncNsExtendedBolusTransaction -import info.nightscout.androidaps.database.transactions.SyncNsOfflineEventTransaction -import info.nightscout.androidaps.database.transactions.SyncNsProfileSwitchTransaction -import info.nightscout.androidaps.database.transactions.SyncNsTemporaryBasalTransaction -import info.nightscout.androidaps.database.transactions.SyncNsTemporaryTargetTransaction -import info.nightscout.androidaps.database.transactions.SyncNsTherapyEventTransaction -import info.nightscout.androidaps.extensions.bolusCalculatorResultFromJson -import info.nightscout.androidaps.extensions.bolusFromJson -import info.nightscout.androidaps.extensions.carbsFromJson -import info.nightscout.androidaps.extensions.effectiveProfileSwitchFromJson -import info.nightscout.androidaps.extensions.extendedBolusFromJson -import info.nightscout.androidaps.extensions.isEffectiveProfileSwitch -import info.nightscout.androidaps.extensions.offlineEventFromJson -import info.nightscout.androidaps.extensions.profileSwitchFromJson -import info.nightscout.androidaps.extensions.temporaryBasalFromJson -import info.nightscout.androidaps.extensions.temporaryTargetFromJson -import info.nightscout.androidaps.extensions.therapyEventFromJson -import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.BuildHelper -import info.nightscout.interfaces.Config -import info.nightscout.androidaps.interfaces.XDripBroadcast -import info.nightscout.androidaps.logging.UserEntryLogger -import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification -import info.nightscout.interfaces.notifications.Notification -import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin -import info.nightscout.androidaps.receivers.DataWorkerStorage -import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.interfaces.utils.JsonHelper -import info.nightscout.interfaces.utils.JsonHelper.safeGetLong -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 java.util.concurrent.TimeUnit -import javax.inject.Inject - -class NSClientAddUpdateWorker( - context: Context, - params: WorkerParameters -) : Worker(context, params) { - - @Inject lateinit var nsClientPlugin: NSClientPlugin - @Inject lateinit var dataWorkerStorage: DataWorkerStorage - @Inject lateinit var aapsLogger: AAPSLogger - @Inject lateinit var buildHelper: BuildHelper - @Inject lateinit var sp: SP - @Inject lateinit var dateUtil: DateUtil - @Inject lateinit var config: Config - @Inject lateinit var repository: AppRepository - @Inject lateinit var activePlugin: ActivePlugin - @Inject lateinit var rxBus: RxBus - @Inject lateinit var uel: UserEntryLogger - @Inject lateinit var virtualPumpPlugin: VirtualPumpPlugin - @Inject lateinit var xDripBroadcast: XDripBroadcast - - override fun doWork(): Result { - val treatments = dataWorkerStorage.pickupJSONArray(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) - ?: return Result.failure(workDataOf("Error" to "missing input data")) - - var ret = Result.success() - var latestDateInReceivedData = 0L - - for (i in 0 until treatments.length()) { - var json = treatments.getJSONObject(i) - aapsLogger.debug(LTag.DATABASE, "Received NS treatment: $json") - - val insulin = JsonHelper.safeGetDouble(json, "insulin") - val carbs = JsonHelper.safeGetDouble(json, "carbs") - var eventType = JsonHelper.safeGetString(json, "eventType") - if (eventType == null) { - aapsLogger.debug(LTag.NSCLIENT, "Wrong treatment. Ignoring : $json") - continue - } - - //Find latest date in treatment - val mills = safeGetLong(json, "mills") - if (mills != 0L && mills < dateUtil.now()) - if (mills > latestDateInReceivedData) latestDateInReceivedData = mills - - if (insulin > 0) { - if (sp.getBoolean(R.string.key_ns_receive_insulin, false) || config.NSCLIENT) { - bolusFromJson(json)?.let { bolus -> - repository.runTransactionForResult(SyncNsBolusTransaction(bolus)) - .doOnError { - aapsLogger.error(LTag.DATABASE, "Error while saving bolus", it) - ret = Result.failure(workDataOf("Error" to it.toString())) - } - .blockingGet() - .also { result -> - result.inserted.forEach { - uel.log( - Action.BOLUS, Sources.NSClient, it.notes, - ValueWithUnit.Timestamp(it.timestamp), - ValueWithUnit.Insulin(it.amount) - ) - aapsLogger.debug(LTag.DATABASE, "Inserted bolus $it") - } - result.invalidated.forEach { - uel.log( - Action.BOLUS_REMOVED, Sources.NSClient, - ValueWithUnit.Timestamp(it.timestamp), - ValueWithUnit.Insulin(it.amount) - ) - aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it") - } - result.updatedNsId.forEach { - aapsLogger.debug(LTag.DATABASE, "Updated nsId of bolus $it") - } - result.updated.forEach { - aapsLogger.debug(LTag.DATABASE, "Updated amount of bolus $it") - } - } - } ?: aapsLogger.error("Error parsing bolus json $json") - } - } - if (carbs > 0) { - if (sp.getBoolean(R.string.key_ns_receive_carbs, false) || config.NSCLIENT) { - carbsFromJson(json)?.let { carb -> - repository.runTransactionForResult(SyncNsCarbsTransaction(carb)) - .doOnError { - aapsLogger.error(LTag.DATABASE, "Error while saving carbs", it) - ret = Result.failure(workDataOf("Error" to it.toString())) - } - .blockingGet() - .also { result -> - result.inserted.forEach { - uel.log( - Action.CARBS, Sources.NSClient, it.notes, - ValueWithUnit.Timestamp(it.timestamp), - ValueWithUnit.Gram(it.amount.toInt()) - ) - aapsLogger.debug(LTag.DATABASE, "Inserted carbs $it") - } - result.invalidated.forEach { - uel.log( - Action.CARBS_REMOVED, Sources.NSClient, - ValueWithUnit.Timestamp(it.timestamp), - ValueWithUnit.Gram(it.amount.toInt()) - ) - aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") - } - result.updated.forEach { - uel.log( - Action.CARBS, Sources.NSClient, it.notes, - ValueWithUnit.Timestamp(it.timestamp), - ValueWithUnit.Gram(it.amount.toInt()) - ) - aapsLogger.debug(LTag.DATABASE, "Updated carbs $it") - } - result.updatedNsId.forEach { - aapsLogger.debug(LTag.DATABASE, "Updated nsId carbs $it") - } - } - } ?: aapsLogger.error("Error parsing bolus json $json") - } - } - // Convert back emulated TBR -> EB - if (eventType == TherapyEvent.Type.TEMPORARY_BASAL.text && json.has("extendedEmulated")) { - val ebJson = json.getJSONObject("extendedEmulated") - ebJson.put("_id", json.getString("_id")) - ebJson.put("isValid", json.getBoolean("isValid")) - ebJson.put("mills", mills) - json = ebJson - eventType = JsonHelper.safeGetString(json, "eventType") - virtualPumpPlugin.fakeDataDetected = true - } - when { - insulin > 0 || carbs > 0 -> Any() - eventType == TherapyEvent.Type.TEMPORARY_TARGET.text -> - if (sp.getBoolean(R.string.key_ns_receive_temp_target, false) || config.NSCLIENT) { - temporaryTargetFromJson(json)?.let { temporaryTarget -> - repository.runTransactionForResult(SyncNsTemporaryTargetTransaction(temporaryTarget)) - .doOnError { - aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) - ret = Result.failure(workDataOf("Error" to it.toString())) - } - .blockingGet() - .also { result -> - result.inserted.forEach { tt -> - uel.log( - Action.TT, Sources.NSClient, - ValueWithUnit.TherapyEventTTReason(tt.reason), - ValueWithUnit.fromGlucoseUnit(tt.lowTarget, Constants.MGDL), - ValueWithUnit.fromGlucoseUnit(tt.highTarget, Constants.MGDL).takeIf { tt.lowTarget != tt.highTarget }, - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tt.duration).toInt()) - ) - aapsLogger.debug(LTag.DATABASE, "Inserted TemporaryTarget $tt") - } - result.invalidated.forEach { tt -> - uel.log( - Action.TT_REMOVED, Sources.NSClient, - ValueWithUnit.TherapyEventTTReason(tt.reason), - ValueWithUnit.Mgdl(tt.lowTarget), - ValueWithUnit.Mgdl(tt.highTarget).takeIf { tt.lowTarget != tt.highTarget }, - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tt.duration).toInt()) - ) - aapsLogger.debug(LTag.DATABASE, "Invalidated TemporaryTarget $tt") - } - result.ended.forEach { tt -> - uel.log( - Action.CANCEL_TT, Sources.NSClient, - ValueWithUnit.TherapyEventTTReason(tt.reason), - ValueWithUnit.Mgdl(tt.lowTarget), - ValueWithUnit.Mgdl(tt.highTarget).takeIf { tt.lowTarget != tt.highTarget }, - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tt.duration).toInt()) - ) - aapsLogger.debug(LTag.DATABASE, "Updated TemporaryTarget $tt") - } - result.updatedNsId.forEach { - aapsLogger.debug(LTag.DATABASE, "Updated nsId TemporaryTarget $it") - } - result.updatedDuration.forEach { - aapsLogger.debug(LTag.DATABASE, "Updated duration TemporaryTarget $it") - } - } - } ?: aapsLogger.error("Error parsing TT json $json") - } - eventType == TherapyEvent.Type.NOTE.text && json.isEffectiveProfileSwitch() -> // replace this by new Type when available in NS - if (sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || config.NSCLIENT) { - effectiveProfileSwitchFromJson(json, dateUtil)?.let { effectiveProfileSwitch -> - repository.runTransactionForResult(SyncNsEffectiveProfileSwitchTransaction(effectiveProfileSwitch)) - .doOnError { - aapsLogger.error(LTag.DATABASE, "Error while saving EffectiveProfileSwitch", it) - ret = Result.failure(workDataOf("Error" to it.toString())) - } - .blockingGet() - .also { result -> - result.inserted.forEach { - uel.log( - Action.PROFILE_SWITCH, Sources.NSClient, - ValueWithUnit.Timestamp(it.timestamp) - ) - aapsLogger.debug(LTag.DATABASE, "Inserted EffectiveProfileSwitch $it") - } - result.invalidated.forEach { - uel.log( - Action.PROFILE_SWITCH_REMOVED, Sources.NSClient, - ValueWithUnit.Timestamp(it.timestamp) - ) - aapsLogger.debug(LTag.DATABASE, "Invalidated EffectiveProfileSwitch $it") - } - result.updatedNsId.forEach { - aapsLogger.debug(LTag.DATABASE, "Updated nsId EffectiveProfileSwitch $it") - } - } - } ?: aapsLogger.error("Error parsing EffectiveProfileSwitch json $json") - } - eventType == TherapyEvent.Type.BOLUS_WIZARD.text -> - bolusCalculatorResultFromJson(json)?.let { bolusCalculatorResult -> - repository.runTransactionForResult(SyncNsBolusCalculatorResultTransaction(bolusCalculatorResult)) - .doOnError { - aapsLogger.error(LTag.DATABASE, "Error while saving BolusCalculatorResult", it) - ret = Result.failure(workDataOf("Error" to it.toString())) - } - .blockingGet() - .also { result -> - result.inserted.forEach { - uel.log( - Action.BOLUS_CALCULATOR_RESULT, Sources.NSClient, - ValueWithUnit.Timestamp(it.timestamp), - ) - aapsLogger.debug(LTag.DATABASE, "Inserted BolusCalculatorResult $it") - } - result.invalidated.forEach { - uel.log( - Action.BOLUS_CALCULATOR_RESULT_REMOVED, Sources.NSClient, - ValueWithUnit.Timestamp(it.timestamp), - ) - aapsLogger.debug(LTag.DATABASE, "Invalidated BolusCalculatorResult $it") - } - result.updatedNsId.forEach { - aapsLogger.debug(LTag.DATABASE, "Updated nsId BolusCalculatorResult $it") - } - } - } ?: aapsLogger.error("Error parsing BolusCalculatorResult json $json") - eventType == TherapyEvent.Type.CANNULA_CHANGE.text || - eventType == TherapyEvent.Type.INSULIN_CHANGE.text || - eventType == TherapyEvent.Type.SENSOR_CHANGE.text || - eventType == TherapyEvent.Type.FINGER_STICK_BG_VALUE.text || - eventType == TherapyEvent.Type.NONE.text || - eventType == TherapyEvent.Type.ANNOUNCEMENT.text || - eventType == TherapyEvent.Type.QUESTION.text || - eventType == TherapyEvent.Type.EXERCISE.text || - eventType == TherapyEvent.Type.NOTE.text || - eventType == TherapyEvent.Type.PUMP_BATTERY_CHANGE.text -> - if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) { - therapyEventFromJson(json)?.let { therapyEvent -> - repository.runTransactionForResult(SyncNsTherapyEventTransaction(therapyEvent)) - .doOnError { - aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) - ret = Result.failure(workDataOf("Error" to it.toString())) - } - .blockingGet() - .also { result -> - val action = when (eventType) { - TherapyEvent.Type.CANNULA_CHANGE.text -> Action.SITE_CHANGE - TherapyEvent.Type.INSULIN_CHANGE.text -> Action.RESERVOIR_CHANGE - else -> Action.CAREPORTAL - } - result.inserted.forEach { therapyEvent -> - uel.log(action, Sources.NSClient, - therapyEvent.note ?: "", - ValueWithUnit.Timestamp(therapyEvent.timestamp), - ValueWithUnit.TherapyEventType(therapyEvent.type), - ValueWithUnit.fromGlucoseUnit(therapyEvent.glucose ?: 0.0, therapyEvent.glucoseUnit.toString).takeIf { therapyEvent.glucose != null } - ) - aapsLogger.debug(LTag.DATABASE, "Inserted TherapyEvent $therapyEvent") - } - result.invalidated.forEach { therapyEvent -> - uel.log(Action.CAREPORTAL_REMOVED, Sources.NSClient, - therapyEvent.note ?: "", - ValueWithUnit.Timestamp(therapyEvent.timestamp), - ValueWithUnit.TherapyEventType(therapyEvent.type), - ValueWithUnit.fromGlucoseUnit(therapyEvent.glucose ?: 0.0, therapyEvent.glucoseUnit.toString).takeIf { therapyEvent.glucose != null } - ) - aapsLogger.debug(LTag.DATABASE, "Invalidated TherapyEvent $therapyEvent") - } - result.updatedNsId.forEach { - aapsLogger.debug(LTag.DATABASE, "Updated nsId TherapyEvent $it") - } - result.updatedDuration.forEach { - aapsLogger.debug(LTag.DATABASE, "Updated nsId TherapyEvent $it") - } - } - } ?: aapsLogger.error("Error parsing TherapyEvent json $json") - } - eventType == TherapyEvent.Type.COMBO_BOLUS.text -> - if (buildHelper.isEngineeringMode() && sp.getBoolean(R.string.key_ns_receive_tbr_eb, false) || config.NSCLIENT) { - extendedBolusFromJson(json)?.let { extendedBolus -> - repository.runTransactionForResult(SyncNsExtendedBolusTransaction(extendedBolus)) - .doOnError { - aapsLogger.error(LTag.DATABASE, "Error while saving extended bolus", it) - ret = Result.failure(workDataOf("Error" to it.toString())) - } - .blockingGet() - .also { result -> - result.inserted.forEach { - uel.log( - Action.EXTENDED_BOLUS, Sources.NSClient, - ValueWithUnit.Timestamp(it.timestamp), - ValueWithUnit.Insulin(it.amount), - ValueWithUnit.UnitPerHour(it.rate), - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) - ) - aapsLogger.debug(LTag.DATABASE, "Inserted ExtendedBolus $it") - } - result.invalidated.forEach { - uel.log( - Action.EXTENDED_BOLUS_REMOVED, Sources.NSClient, - ValueWithUnit.Timestamp(it.timestamp), - ValueWithUnit.Insulin(it.amount), - ValueWithUnit.UnitPerHour(it.rate), - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) - ) - aapsLogger.debug(LTag.DATABASE, "Invalidated ExtendedBolus $it") - } - result.ended.forEach { - uel.log( - Action.CANCEL_EXTENDED_BOLUS, Sources.NSClient, - ValueWithUnit.Timestamp(it.timestamp), - ValueWithUnit.Insulin(it.amount), - ValueWithUnit.UnitPerHour(it.rate), - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) - ) - aapsLogger.debug(LTag.DATABASE, "Updated ExtendedBolus $it") - } - result.updatedNsId.forEach { - aapsLogger.debug(LTag.DATABASE, "Updated nsId ExtendedBolus $it") - } - result.updatedDuration.forEach { - aapsLogger.debug(LTag.DATABASE, "Updated duration ExtendedBolus $it") - } - } - } ?: aapsLogger.error("Error parsing ExtendedBolus json $json") - } - eventType == TherapyEvent.Type.TEMPORARY_BASAL.text -> - if (buildHelper.isEngineeringMode() && sp.getBoolean(R.string.key_ns_receive_tbr_eb, false) || config.NSCLIENT) { - temporaryBasalFromJson(json)?.let { temporaryBasal -> - repository.runTransactionForResult(SyncNsTemporaryBasalTransaction(temporaryBasal)) - .doOnError { - aapsLogger.error(LTag.DATABASE, "Error while saving temporary basal", it) - ret = Result.failure(workDataOf("Error" to it.toString())) - } - .blockingGet() - .also { result -> - result.inserted.forEach { - uel.log( - Action.TEMP_BASAL, Sources.NSClient, - ValueWithUnit.Timestamp(it.timestamp), - if (it.isAbsolute) ValueWithUnit.UnitPerHour(it.rate) else ValueWithUnit.Percent(it.rate.toInt()), - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) - ) - aapsLogger.debug(LTag.DATABASE, "Inserted TemporaryBasal $it") - } - result.invalidated.forEach { - uel.log( - Action.TEMP_BASAL_REMOVED, Sources.NSClient, - ValueWithUnit.Timestamp(it.timestamp), - if (it.isAbsolute) ValueWithUnit.UnitPerHour(it.rate) else ValueWithUnit.Percent(it.rate.toInt()), - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) - ) - aapsLogger.debug(LTag.DATABASE, "Invalidated TemporaryBasal $it") - } - result.ended.forEach { - uel.log( - Action.CANCEL_TEMP_BASAL, Sources.NSClient, - ValueWithUnit.Timestamp(it.timestamp), - if (it.isAbsolute) ValueWithUnit.UnitPerHour(it.rate) else ValueWithUnit.Percent(it.rate.toInt()), - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) - ) - aapsLogger.debug(LTag.DATABASE, "Ended TemporaryBasal $it") - } - result.updatedNsId.forEach { - aapsLogger.debug(LTag.DATABASE, "Updated nsId TemporaryBasal $it") - } - result.updatedDuration.forEach { - aapsLogger.debug(LTag.DATABASE, "Updated duration TemporaryBasal $it") - } - } - } ?: aapsLogger.error("Error parsing TemporaryBasal json $json") - } - eventType == TherapyEvent.Type.PROFILE_SWITCH.text -> - if (sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || config.NSCLIENT) { - profileSwitchFromJson(json, dateUtil, activePlugin)?.let { profileSwitch -> - repository.runTransactionForResult(SyncNsProfileSwitchTransaction(profileSwitch)) - .doOnError { - aapsLogger.error(LTag.DATABASE, "Error while saving ProfileSwitch", it) - ret = Result.failure(workDataOf("Error" to it.toString())) - } - .blockingGet() - .also { result -> - result.inserted.forEach { - uel.log( - Action.PROFILE_SWITCH, Sources.NSClient, - ValueWithUnit.Timestamp(it.timestamp) - ) - aapsLogger.debug(LTag.DATABASE, "Inserted ProfileSwitch $it") - } - result.invalidated.forEach { - uel.log( - Action.PROFILE_SWITCH_REMOVED, Sources.NSClient, - ValueWithUnit.Timestamp(it.timestamp) - ) - aapsLogger.debug(LTag.DATABASE, "Invalidated ProfileSwitch $it") - } - result.updatedNsId.forEach { - aapsLogger.debug(LTag.DATABASE, "Updated nsId ProfileSwitch $it") - } - } - } ?: aapsLogger.error("Error parsing ProfileSwitch json $json") - } - eventType == TherapyEvent.Type.APS_OFFLINE.text -> - if (sp.getBoolean(R.string.key_ns_receive_offline_event, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) { - offlineEventFromJson(json)?.let { offlineEvent -> - repository.runTransactionForResult(SyncNsOfflineEventTransaction(offlineEvent)) - .doOnError { - aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) - ret = Result.failure(workDataOf("Error" to it.toString())) - } - .blockingGet() - .also { result -> - result.inserted.forEach { oe -> - uel.log( - Action.LOOP_CHANGE, Sources.NSClient, - ValueWithUnit.OfflineEventReason(oe.reason), - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt()) - ) - aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $oe") - } - result.invalidated.forEach { oe -> - uel.log( - Action.LOOP_REMOVED, Sources.NSClient, - ValueWithUnit.OfflineEventReason(oe.reason), - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt()) - ) - aapsLogger.debug(LTag.DATABASE, "Invalidated OfflineEvent $oe") - } - result.ended.forEach { oe -> - uel.log( - Action.LOOP_CHANGE, Sources.NSClient, - ValueWithUnit.OfflineEventReason(oe.reason), - ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt()) - ) - aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $oe") - } - result.updatedNsId.forEach { - aapsLogger.debug(LTag.DATABASE, "Updated nsId OfflineEvent $it") - } - result.updatedDuration.forEach { - aapsLogger.debug(LTag.DATABASE, "Updated duration OfflineEvent $it") - } - } - } ?: aapsLogger.error("Error parsing OfflineEvent json $json") - } - } - if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) - if (eventType == TherapyEvent.Type.ANNOUNCEMENT.text) { - val date = safeGetLong(json, "mills") - val now = System.currentTimeMillis() - val enteredBy = JsonHelper.safeGetString(json, "enteredBy", "") - val notes = JsonHelper.safeGetString(json, "notes", "") - if (date > now - 15 * 60 * 1000L && notes.isNotEmpty() - && enteredBy != sp.getString("careportal_enteredby", "AndroidAPS") - ) { - val defaultVal = config.NSCLIENT - if (sp.getBoolean(R.string.key_ns_announcements, defaultVal)) { - val announcement = Notification(Notification.NS_ANNOUNCEMENT, notes, Notification.ANNOUNCEMENT, 60) - rxBus.send(EventNewNotification(announcement)) - } - } - } - } - nsClientPlugin.updateLatestDateReceivedIfNewer(latestDateInReceivedData) - xDripBroadcast.sendTreatments(treatments) - return ret - } - - init { - (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/DeviceStatusData.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/DeviceStatusData.kt deleted file mode 100644 index a4b1b7ff7e..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/DeviceStatusData.kt +++ /dev/null @@ -1,42 +0,0 @@ -package info.nightscout.androidaps.plugins.general.nsclient.data - -import android.text.Spanned -import org.json.JSONObject -import java.util.HashMap -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class DeviceStatusData @Inject constructor() { - - class PumpData { - var clock = 0L - var isPercent = false - var percent = 0 - var voltage = 0.0 - var status = "N/A" - var reservoir = 0.0 - var reservoirDisplayOverride = "" - var extended: Spanned? = null - var activeProfileName: String? = null - } - - var pumpData: PumpData? = null - - class Uploader { - var clock = 0L - var battery = 0 - } - - val uploaderMap = HashMap() - - class OpenAPSData { - var clockSuggested = 0L - var clockEnacted = 0L - var suggested: JSONObject? = null - var enacted: JSONObject? = null - } - - var openAPSData = OpenAPSData() -} - diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSDeviceStatus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSDeviceStatus.kt deleted file mode 100644 index 88d65bd314..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSDeviceStatus.kt +++ /dev/null @@ -1,375 +0,0 @@ -package info.nightscout.androidaps.plugins.general.nsclient.data - -import android.text.Spanned -import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.R -import info.nightscout.interfaces.Config -import info.nightscout.androidaps.interfaces.ResourceHelper -import info.nightscout.androidaps.plugins.aps.loop.APSResult -import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration -import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.interfaces.utils.HtmlHelper.fromHtml -import info.nightscout.interfaces.utils.JsonHelper -import info.nightscout.interfaces.utils.Round -import info.nightscout.androidaps.utils.T -import info.nightscout.rx.logging.AAPSLogger -import info.nightscout.rx.logging.LTag -import info.nightscout.shared.sharedPreferences.SP -import org.json.JSONArray -import org.json.JSONException -import org.json.JSONObject -import javax.inject.Inject -import javax.inject.Singleton - -/* -{ - "_id": "594fdcec327b83c81b6b8c0f", - "device": "openaps://Sony D5803", - "pump": { - "battery": { - "percent": 100 - }, - "status": { - "status": "normal", - "timestamp": "2017-06-25T15:50:14Z" - }, - "extended": { - "Version": "1.5-ac98852-2017.06.25", - "PumpIOB": 1.13, - "LastBolus": "25. 6. 2017 17:25:00", - "LastBolusAmount": 0.3, - "BaseBasalRate": 0.4, - "ActiveProfile": "2016 +30%" - }, - "reservoir": 109, - "clock": "2017-06-25T15:55:10Z" - }, - "openaps": { - "suggested": { - "temp": "absolute", - "bg": 115.9, - "tick": "+5", - "eventualBG": 105, - "snoozeBG": 105, - "predBGs": { - "IOB": [116, 114, 112, 110, 109, 107, 106, 105, 105, 104, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 106, 106, 106, 106, 106, 107] - }, - "sensitivityRatio": 0.81, - "variable_sens": 137.3, - "COB": 0, - "IOB": -0.035, - "reason": "COB: 0, Dev: -18, BGI: 0.43, ISF: 216, Target: 99; Eventual BG 105 > 99 but Min. Delta -2.60 < Exp. Delta 0.1; setting current basal of 0.4 as temp. Suggested rate is same as profile rate, no temp basal is active, doing nothing", - "timestamp": "2017-06-25T15:55:10Z" - }, - "iob": { - "iob": -0.035, - "basaliob": -0.035, - "activity": -0.0004, - "time": "2017-06-25T15:55:10Z" - } - }, - "uploaderBattery": 93, - "created_at": "2017-06-25T15:55:10Z", - "NSCLIENT_ID": 1498406118857 -} - */ -@Suppress("SpellCheckingInspection") -@Singleton -class NSDeviceStatus @Inject constructor( - private val aapsLogger: AAPSLogger, - private val sp: SP, - private val rh: ResourceHelper, - private val nsSettingsStatus: NSSettingsStatus, - private val config: Config, - private val dateUtil: DateUtil, - private val runningConfiguration: RunningConfiguration, - private val deviceStatusData: DeviceStatusData -) { - - private var data: JSONObject? = null - fun handleNewData(deviceStatuses: JSONArray) { - aapsLogger.debug(LTag.NSCLIENT, "Got NS deviceStatus: \$deviceStatuses") - try { - for (i in deviceStatuses.length() - 1 downTo 0) { - val devicestatusJson = deviceStatuses.getJSONObject(i) - if (devicestatusJson != null) { - setData(devicestatusJson) - if (devicestatusJson.has("pump")) { - // Objectives 0 - sp.putBoolean(R.string.key_ObjectivespumpStatusIsAvailableInNS, true) - } - if (devicestatusJson.has("configuration") && config.NSCLIENT) { - // copy configuration of Insulin and Sensitivity from main AAPS - runningConfiguration.apply(devicestatusJson.getJSONObject("configuration")) - break - } - } - } - } catch (jsonException: JSONException) { - jsonException.printStackTrace() - } - } - - private fun setData(obj: JSONObject): NSDeviceStatus { - data = obj - updatePumpData() - updateOpenApsData(obj) - updateUploaderData(obj) - return this - } - - val device: String - get() { - try { - if (data!!.has("device")) { - var device = data!!.getString("device") - if (device.startsWith("openaps://")) { - device = device.substring(10) - return device - } - } - } catch (e: JSONException) { - aapsLogger.error("Unhandled exception", e) - } - return "" - } - - enum class Levels(val level: Int) { - - URGENT(2), - WARN(1), - INFO(0); - - fun toColor(): String = - when (level) { - INFO.level -> "white" - WARN.level -> "yellow" - URGENT.level -> "red" - else -> "white" - } - } - - val extendedPumpStatus: Spanned - get() = deviceStatusData.pumpData?.extended ?: fromHtml("") - - val pumpStatus: Spanned - // test warning level // color - get() { - val pumpData = deviceStatusData.pumpData ?: return fromHtml("") - - //String[] ALL_STATUS_FIELDS = {"reservoir", "battery", "clock", "status", "device"}; - val string = StringBuilder() - .append("") - .append(rh.gs(R.string.pump)) - .append(": ") - - // test warning level - val level = when { - pumpData.clock + nsSettingsStatus.extendedPumpSettings("urgentClock") * 60 * 1000L < dateUtil.now() -> Levels.URGENT - pumpData.reservoir < nsSettingsStatus.extendedPumpSettings("urgentRes") -> Levels.URGENT - pumpData.isPercent && pumpData.percent < nsSettingsStatus.extendedPumpSettings("urgentBattP") -> Levels.URGENT - !pumpData.isPercent && pumpData.voltage > 0 && pumpData.voltage < nsSettingsStatus.extendedPumpSettings("urgentBattV") -> Levels.URGENT - pumpData.clock + nsSettingsStatus.extendedPumpSettings("warnClock") * 60 * 1000L < dateUtil.now() -> Levels.WARN - pumpData.reservoir < nsSettingsStatus.extendedPumpSettings("warnRes") -> Levels.WARN - pumpData.isPercent && pumpData.percent < nsSettingsStatus.extendedPumpSettings("warnBattP") -> Levels.WARN - !pumpData.isPercent && pumpData.voltage > 0 && pumpData.voltage < nsSettingsStatus.extendedPumpSettings("warnBattV") -> Levels.WARN - else -> Levels.INFO - } - string.append("") - val insulinUnit = rh.gs(R.string.insulin_unit_shortname) - val fields = nsSettingsStatus.pumpExtendedSettingsFields() - if (pumpData.reservoirDisplayOverride != "") { - string.append(pumpData.reservoirDisplayOverride).append("$insulinUnit ") - } - else if (fields.contains("reservoir")) string.append(pumpData.reservoir.toInt()).append("$insulinUnit ") - if (fields.contains("battery") && pumpData.isPercent) string.append(pumpData.percent).append("% ") - if (fields.contains("battery") && !pumpData.isPercent) string.append(Round.roundTo(pumpData.voltage, 0.001)).append(" ") - if (fields.contains("clock")) string.append(dateUtil.minAgo(rh, pumpData.clock)).append(" ") - if (fields.contains("status")) string.append(pumpData.status).append(" ") - if (fields.contains("device")) string.append(device).append(" ") - string.append("") // color - return fromHtml(string.toString()) - } - - private fun updatePumpData() { - try { - val data = this.data ?: return - val pump = if (data.has("pump")) data.getJSONObject("pump") else JSONObject() - val clock = if (pump.has("clock")) dateUtil.fromISODateString(pump.getString("clock")) else 0L - // check if this is new data - if (clock == 0L || deviceStatusData.pumpData != null && clock < deviceStatusData.pumpData!!.clock) return - - // create new status and process data - val deviceStatusPumpData = DeviceStatusData.PumpData() - deviceStatusPumpData.clock = clock - if (pump.has("status") && pump.getJSONObject("status").has("status")) deviceStatusPumpData.status = pump.getJSONObject("status").getString("status") - if (pump.has("reservoir")) deviceStatusPumpData.reservoir = pump.getDouble("reservoir") - if (pump.has("reservoir_display_override")) deviceStatusPumpData.reservoirDisplayOverride = pump.getString("reservoir_display_override") - if (pump.has("battery") && pump.getJSONObject("battery").has("percent")) { - deviceStatusPumpData.isPercent = true - deviceStatusPumpData.percent = pump.getJSONObject("battery").getInt("percent") - } else if (pump.has("battery") && pump.getJSONObject("battery").has("voltage")) { - deviceStatusPumpData.isPercent = false - deviceStatusPumpData.voltage = pump.getJSONObject("battery").getDouble("voltage") - } - if (pump.has("extended")) { - val extendedJson = pump.getJSONObject("extended") - val extended = StringBuilder() - val keys: Iterator<*> = extendedJson.keys() - while (keys.hasNext()) { - val key = keys.next() as String - val value = extendedJson.getString(key) - extended.append("").append(key).append(": ").append(value).append("
") - } - deviceStatusPumpData.extended = fromHtml(extended.toString()) - deviceStatusPumpData.activeProfileName = JsonHelper.safeGetStringAllowNull(extendedJson, "ActiveProfile", null) - } - deviceStatusData.pumpData = deviceStatusPumpData - } catch (e: Exception) { - aapsLogger.error("Unhandled exception", e) - } - } - - private fun updateOpenApsData(jsonObject: JSONObject) { - try { - val openAps = if (jsonObject.has("openaps")) jsonObject.getJSONObject("openaps") else JSONObject() - val suggested = if (openAps.has("suggested")) openAps.getJSONObject("suggested") else JSONObject() - val enacted = if (openAps.has("enacted")) openAps.getJSONObject("enacted") else JSONObject() - var clock = if (suggested.has("timestamp")) dateUtil.fromISODateString(suggested.getString("timestamp")) else 0L - // check if this is new data - if (clock != 0L && clock > deviceStatusData.openAPSData.clockSuggested) { - deviceStatusData.openAPSData.suggested = suggested - deviceStatusData.openAPSData.clockSuggested = clock - } - clock = if (enacted.has("timestamp")) dateUtil.fromISODateString(enacted.getString("timestamp")) else 0L - // check if this is new data - if (clock != 0L && clock > deviceStatusData.openAPSData.clockEnacted) { - deviceStatusData.openAPSData.enacted = enacted - deviceStatusData.openAPSData.clockEnacted = clock - } - } catch (e: Exception) { - aapsLogger.error("Unhandled exception", e) - } - } - - val openApsStatus: Spanned - get() { - val string = StringBuilder() - .append("") - .append(rh.gs(R.string.openaps_short)) - .append(": ") - - // test warning level - val level = when { - deviceStatusData.openAPSData.clockSuggested + T.mins(sp.getLong(R.string.key_nsalarm_urgent_staledatavalue, 31)).msecs() < dateUtil.now() -> Levels.URGENT - deviceStatusData.openAPSData.clockSuggested + T.mins(sp.getLong(R.string.key_nsalarm_staledatavalue, 16)).msecs() < dateUtil.now() -> Levels.WARN - else -> Levels.INFO - } - string.append("") - if (deviceStatusData.openAPSData.clockSuggested != 0L) string.append(dateUtil.minAgo(rh, deviceStatusData.openAPSData.clockSuggested)).append(" ") - string.append("") // color - return fromHtml(string.toString()) - } - - val extendedOpenApsStatus: Spanned - get() { - val string = StringBuilder() - try { - if (deviceStatusData.openAPSData.enacted != null && deviceStatusData.openAPSData.clockEnacted != deviceStatusData.openAPSData.clockSuggested) string.append("") - .append(dateUtil.minAgo(rh, deviceStatusData.openAPSData.clockEnacted)).append(" ").append(deviceStatusData.openAPSData.enacted!!.getString("reason")).append("
") - if (deviceStatusData.openAPSData.suggested != null) string.append("").append(dateUtil.minAgo(rh, deviceStatusData.openAPSData.clockSuggested)).append(" ") - .append(deviceStatusData.openAPSData.suggested!!.getString("reason")).append("
") - return fromHtml(string.toString()) - } catch (e: JSONException) { - aapsLogger.error("Unhandled exception", e) - } - return fromHtml("") - } - - private fun updateUploaderData(jsonObject: JSONObject) { - try { - val clock = - when { - jsonObject.has("mills") -> jsonObject.getLong("mills") - jsonObject.has("created_at") -> dateUtil.fromISODateString(jsonObject.getString("created_at")) - else -> 0L - } - val device = device - val battery: Int = - when { - jsonObject.has("uploaderBattery") -> jsonObject.getInt("uploaderBattery") - jsonObject.has("uploader") && jsonObject.getJSONObject("uploader").has("battery") -> jsonObject.getJSONObject("uploader").getInt("battery") - else -> 0 - } - - var uploader = deviceStatusData.uploaderMap[device] - // check if this is new data - if (clock != 0L && battery != 0 && (uploader == null || clock > uploader.clock)) { - if (uploader == null) uploader = DeviceStatusData.Uploader() - uploader.battery = battery - uploader.clock = clock - deviceStatusData.uploaderMap[device] = uploader - } - } catch (e: Exception) { - aapsLogger.error("Unhandled exception", e) - } - } - - val uploaderStatus: String - get() { - val iterator: Iterator<*> = deviceStatusData.uploaderMap.entries.iterator() - var minBattery = 100 - while (iterator.hasNext()) { - val pair = iterator.next() as Map.Entry<*, *> - val uploader = pair.value as DeviceStatusData.Uploader - if (minBattery > uploader.battery) minBattery = uploader.battery - } - return "$minBattery%" - } - - val uploaderStatusSpanned: Spanned - get() { - val string = StringBuilder() - string.append("") - string.append(rh.gs(R.string.uploader_short)) - string.append(": ") - val iterator: Iterator<*> = deviceStatusData.uploaderMap.entries.iterator() - var minBattery = 100 - while (iterator.hasNext()) { - val pair = iterator.next() as Map.Entry<*, *> - val uploader = pair.value as DeviceStatusData.Uploader - if (minBattery > uploader.battery) minBattery = uploader.battery - } - string.append(minBattery) - string.append("%") - return fromHtml(string.toString()) - } - - val extendedUploaderStatus: Spanned - get() { - val string = StringBuilder() - val iterator: Iterator<*> = deviceStatusData.uploaderMap.entries.iterator() - while (iterator.hasNext()) { - val pair = iterator.next() as Map.Entry<*, *> - val uploader = pair.value as DeviceStatusData.Uploader - val device = pair.key as String - string.append("").append(device).append(": ").append(uploader.battery).append("%
") - } - return fromHtml(string.toString()) - } - - val openApsTimestamp: Long - get() = - if (deviceStatusData.openAPSData.clockSuggested != 0L) { - deviceStatusData.openAPSData.clockSuggested - } else { - -1 - } - - fun getAPSResult(injector: HasAndroidInjector): APSResult { - val result = APSResult(injector) - result.json = deviceStatusData.openAPSData.suggested - result.date = deviceStatusData.openAPSData.clockSuggested - return result - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/events/EventNSClientStatus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/events/EventNSClientStatus.kt deleted file mode 100644 index abee8602e4..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/events/EventNSClientStatus.kt +++ /dev/null @@ -1,8 +0,0 @@ -package info.nightscout.androidaps.plugins.general.nsclient.events - -import info.nightscout.androidaps.events.EventStatus -import info.nightscout.androidaps.interfaces.ResourceHelper - -class EventNSClientStatus(var text: String) : EventStatus() { - override fun getStatus(rh: ResourceHelper): String = text -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/events/EventNSClientUpdateGUI.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/events/EventNSClientUpdateGUI.kt deleted file mode 100644 index 66ecdc7787..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/events/EventNSClientUpdateGUI.kt +++ /dev/null @@ -1,5 +0,0 @@ -package info.nightscout.androidaps.plugins.general.nsclient.events - -import info.nightscout.rx.events.EventUpdateGui - -class EventNSClientUpdateGUI : EventUpdateGui() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt index 5b2af79806..26aa62ac2d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt @@ -27,7 +27,6 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.jjoe64.graphview.GraphView import dagger.android.HasAndroidInjector import dagger.android.support.DaggerFragment -import info.nightscout.interfaces.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.database.AppRepository @@ -51,9 +50,7 @@ import info.nightscout.androidaps.extensions.runOnUiThread import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.valueToUnitsString import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.BuildHelper import info.nightscout.androidaps.interfaces.CommandQueue -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraints import info.nightscout.androidaps.interfaces.GlucoseUnit @@ -67,7 +64,6 @@ import info.nightscout.androidaps.interfaces.TrendCalculator import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalResultSMB -import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverviewCalcProgress import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverviewGraph @@ -81,11 +77,12 @@ import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin import info.nightscout.androidaps.plugins.source.DexcomPlugin import info.nightscout.androidaps.plugins.source.XdripPlugin +import info.nightscout.androidaps.plugins.sync.nsclient.data.NSSettingsStatus +import info.nightscout.androidaps.plugins.sync.nsclient.data.ProcessedDeviceStatusData import info.nightscout.androidaps.skins.SkinProvider import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.interfaces.utils.JsonHelper import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.protection.ProtectionCheck @@ -93,6 +90,10 @@ import info.nightscout.androidaps.utils.ui.SingleClickButton import info.nightscout.androidaps.utils.ui.UIRunnable import info.nightscout.androidaps.utils.wizard.QuickWizard import info.nightscout.automation.AutomationPlugin +import info.nightscout.interfaces.BuildHelper +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.Constants +import info.nightscout.interfaces.utils.JsonHelper import info.nightscout.plugins.constraints.bgQualityCheck.BgQualityCheckPlugin import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus @@ -128,7 +129,8 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var constraintChecker: Constraints @Inject lateinit var statusLightHandler: StatusLightHandler - @Inject lateinit var nsDeviceStatus: NSDeviceStatus + @Inject lateinit var processedDeviceStatusData: ProcessedDeviceStatusData + @Inject lateinit var nsSettingsStatus: NSSettingsStatus @Inject lateinit var loop: Loop @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var iobCobCalculator: IobCobCalculator @@ -722,16 +724,16 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList } // pump status from ns - binding.pump.text = nsDeviceStatus.pumpStatus - binding.pump.setOnClickListener { activity?.let { OKDialog.show(it, rh.gs(R.string.pump), nsDeviceStatus.extendedPumpStatus) } } + binding.pump.text = processedDeviceStatusData.pumpStatus(nsSettingsStatus) + binding.pump.setOnClickListener { activity?.let { OKDialog.show(it, rh.gs(R.string.pump), processedDeviceStatusData.extendedPumpStatus) } } // OpenAPS status from ns - binding.openaps.text = nsDeviceStatus.openApsStatus - binding.openaps.setOnClickListener { activity?.let { OKDialog.show(it, rh.gs(R.string.openaps), nsDeviceStatus.extendedOpenApsStatus) } } + binding.openaps.text = processedDeviceStatusData.openApsStatus + binding.openaps.setOnClickListener { activity?.let { OKDialog.show(it, rh.gs(R.string.openaps), processedDeviceStatusData.extendedOpenApsStatus) } } // Uploader status from ns - binding.uploader.text = nsDeviceStatus.uploaderStatusSpanned - binding.uploader.setOnClickListener { activity?.let { OKDialog.show(it, rh.gs(R.string.uploader), nsDeviceStatus.extendedUploaderStatus) } } + binding.uploader.text = processedDeviceStatusData.uploaderStatusSpanned + binding.uploader.setOnClickListener { activity?.let { OKDialog.show(it, rh.gs(R.string.uploader), processedDeviceStatusData.extendedUploaderStatus) } } } } @@ -1118,7 +1120,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList val isfMgdl = profile?.getIsfMgdl() val variableSens = if (config.APS && request is DetermineBasalResultSMB) request.variableSens ?: 0.0 - else if (config.NSCLIENT) JsonHelper.safeGetDouble(nsDeviceStatus.getAPSResult(injector).json, "variable_sens") + else if (config.NSCLIENT) JsonHelper.safeGetDouble(processedDeviceStatusData.getAPSResult(injector).json, "variable_sens") else 0.0 if (variableSens != isfMgdl && variableSens != 0.0 && isfMgdl != null) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt index c33faafdc5..2ad816f816 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt @@ -6,17 +6,15 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.extensions.age +import info.nightscout.plugins.sync.nsclient.extensions.age import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.interfaces.Config import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin -import info.nightscout.androidaps.plugins.pump.omnipod.eros.driver.definition.OmnipodConstants import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.WarnColors import info.nightscout.androidaps.interfaces.ResourceHelper -import info.nightscout.androidaps.plugins.pump.eopatch.AppConstant import info.nightscout.shared.sharedPreferences.SP import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationWithAction.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationWithAction.kt index dcd758ea16..1aa1a7e0b1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationWithAction.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationWithAction.kt @@ -3,8 +3,8 @@ package info.nightscout.androidaps.plugins.general.overview.notifications import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.interfaces.ResourceHelper -import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin -import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm +import info.nightscout.androidaps.plugins.sync.nsclient.NSClientPlugin +import info.nightscout.androidaps.plugins.sync.nsclient.data.NSAlarm import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.T import info.nightscout.interfaces.notifications.Notification diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearFragment.kt index 67864710ff..37f6193315 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearFragment.kt @@ -6,7 +6,7 @@ import android.view.View import android.view.ViewGroup import dagger.android.support.DaggerFragment import info.nightscout.androidaps.databinding.WearFragmentBinding -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientUpdateGUI import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.rx.AapsSchedulers @@ -65,7 +65,7 @@ class WearFragment : DaggerFragment() { _binding = null } - fun updateGui() { + private fun updateGui() { _binding ?: return binding.connectedDevice.text = wearPlugin.connectedDevice } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt index 58f4618744..dbb01a3e57 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt @@ -3,12 +3,17 @@ package info.nightscout.androidaps.plugins.general.wear.wearintegration import android.app.NotificationManager import android.content.Context import dagger.android.HasAndroidInjector -import info.nightscout.interfaces.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper -import info.nightscout.androidaps.database.entities.* +import info.nightscout.androidaps.database.entities.Bolus +import info.nightscout.androidaps.database.entities.GlucoseValue +import info.nightscout.androidaps.database.entities.TemporaryBasal +import info.nightscout.androidaps.database.entities.TemporaryTarget +import info.nightscout.androidaps.database.entities.TotalDailyDose +import info.nightscout.androidaps.database.entities.UserEntry +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction @@ -18,19 +23,37 @@ import info.nightscout.androidaps.extensions.toStringShort import info.nightscout.androidaps.extensions.total import info.nightscout.androidaps.extensions.valueToUnits import info.nightscout.androidaps.extensions.valueToUnitsString -import info.nightscout.androidaps.interfaces.* +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.CommandQueue +import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.interfaces.Constraints +import info.nightscout.androidaps.interfaces.GlucoseUnit +import info.nightscout.androidaps.interfaces.IobCobCalculator +import info.nightscout.androidaps.interfaces.Loop +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.androidaps.interfaces.TrendCalculator import info.nightscout.androidaps.logging.UserEntryLogger -import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus import info.nightscout.androidaps.plugins.general.overview.graphExtensions.GlucoseValueDataPoint import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider -import info.nightscout.interfaces.queue.Callback +import info.nightscout.androidaps.plugins.sync.nsclient.data.ProcessedDeviceStatusData import info.nightscout.androidaps.receivers.ReceiverStatusStore import info.nightscout.androidaps.services.AlarmSoundServiceHelper -import info.nightscout.androidaps.utils.* +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.DecimalFormatter +import info.nightscout.androidaps.utils.DefaultValueHelper +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.HardLimits +import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.wizard.BolusWizard import info.nightscout.androidaps.utils.wizard.QuickWizard import info.nightscout.androidaps.utils.wizard.QuickWizardEntry import info.nightscout.interfaces.Config +import info.nightscout.interfaces.Constants +import info.nightscout.interfaces.queue.Callback import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventMobileToWear @@ -43,7 +66,9 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import java.text.DateFormat import java.text.SimpleDateFormat -import java.util.* +import java.util.Date +import java.util.LinkedList +import java.util.Locale import java.util.concurrent.TimeUnit import java.util.stream.Collectors import javax.inject.Inject @@ -66,7 +91,7 @@ class DataHandlerMobile @Inject constructor( private val glucoseStatusProvider: GlucoseStatusProvider, private val profileFunction: ProfileFunction, private val loop: Loop, - private val nsDeviceStatus: NSDeviceStatus, + private val processedDeviceStatusData: ProcessedDeviceStatusData, private val receiverStatusStore: ReceiverStatusStore, private val quickWizard: QuickWizard, private val defaultValueHelper: DefaultValueHelper, @@ -872,11 +897,11 @@ class DataHandlerMobile @Inject constructor( //batteries val phoneBattery = receiverStatusStore.batteryLevel - val rigBattery = nsDeviceStatus.uploaderStatus.trim { it <= ' ' } + val rigBattery = processedDeviceStatusData.uploaderStatus.trim { it <= ' ' } //OpenAPS status val openApsStatus = if (config.APS) loop.lastRun?.let { if (it.lastTBREnact != 0L) it.lastTBREnact else -1 } ?: -1 - else nsDeviceStatus.openApsTimestamp + else processedDeviceStatusData.openApsTimestamp rxBus.send( EventMobileToWear( diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityAAPSPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityAAPSPlugin.kt index f1b93bc12a..2f3d6cd790 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityAAPSPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityAAPSPlugin.kt @@ -1,26 +1,25 @@ package info.nightscout.androidaps.plugins.sensitivity import dagger.android.HasAndroidInjector -import info.nightscout.interfaces.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.annotations.OpenForTesting import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.extensions.isPSEvent5minBack -import info.nightscout.androidaps.extensions.isTherapyEventEvent5minBack -import info.nightscout.interfaces.PluginDescription -import info.nightscout.interfaces.PluginType import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.interfaces.Sensitivity.SensitivityType import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult +import info.nightscout.plugins.sync.nsclient.extensions.isTherapyEventEvent5minBack import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.extensions.isPSEvent5minBack +import info.nightscout.interfaces.Constants +import info.nightscout.interfaces.PluginDescription +import info.nightscout.interfaces.PluginType import info.nightscout.plugins.utils.Percentile import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag - import info.nightscout.shared.sharedPreferences.SP import org.json.JSONException import org.json.JSONObject diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref1Plugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref1Plugin.kt index 30a97b3bd5..8e56dd9433 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref1Plugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref1Plugin.kt @@ -1,27 +1,26 @@ package info.nightscout.androidaps.plugins.sensitivity import dagger.android.HasAndroidInjector -import info.nightscout.interfaces.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.annotations.OpenForTesting import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.extensions.isPSEvent5minBack -import info.nightscout.androidaps.extensions.isTherapyEventEvent5minBack -import info.nightscout.interfaces.PluginDescription -import info.nightscout.interfaces.PluginType import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.interfaces.Sensitivity.SensitivityType -import info.nightscout.interfaces.aps.SMBDefaults import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult +import info.nightscout.plugins.sync.nsclient.extensions.isTherapyEventEvent5minBack import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.extensions.isPSEvent5minBack +import info.nightscout.interfaces.Constants +import info.nightscout.interfaces.PluginDescription +import info.nightscout.interfaces.PluginType +import info.nightscout.interfaces.aps.SMBDefaults import info.nightscout.plugins.utils.Percentile import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag - import info.nightscout.shared.sharedPreferences.SP import org.json.JSONException import org.json.JSONObject diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityWeightedAveragePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityWeightedAveragePlugin.kt index fba1e6e97d..91cfd402ce 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityWeightedAveragePlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityWeightedAveragePlugin.kt @@ -2,22 +2,22 @@ package info.nightscout.androidaps.plugins.sensitivity import androidx.collection.LongSparseArray import dagger.android.HasAndroidInjector -import info.nightscout.interfaces.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.annotations.OpenForTesting import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.extensions.isPSEvent5minBack -import info.nightscout.androidaps.extensions.isTherapyEventEvent5minBack -import info.nightscout.interfaces.PluginDescription -import info.nightscout.interfaces.PluginType import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.interfaces.Sensitivity.SensitivityType import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult +import info.nightscout.plugins.sync.nsclient.extensions.isTherapyEventEvent5minBack import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.extensions.isPSEvent5minBack +import info.nightscout.interfaces.Constants +import info.nightscout.interfaces.PluginDescription +import info.nightscout.interfaces.PluginType import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag import info.nightscout.shared.sharedPreferences.SP @@ -147,13 +147,16 @@ class SensitivityWeightedAveragePlugin @Inject constructor( else -> "Sensitivity normal" } aapsLogger.debug(LTag.AUTOSENS, sensResult) - val output = fillResult(ratio, current.cob, pastSensitivity, ratioLimit, - sensResult, data.size()) + val output = fillResult( + ratio, current.cob, pastSensitivity, ratioLimit, + sensResult, data.size() + ) aapsLogger.debug( LTag.AUTOSENS, "Sensitivity to: " - + dateUtil.dateAndTimeString(toTime) + - " ratio: " + output.ratio - + " mealCOB: " + current.cob) + + dateUtil.dateAndTimeString(toTime) + + " ratio: " + output.ratio + + " mealCOB: " + current.cob + ) return output } @@ -178,7 +181,10 @@ class SensitivityWeightedAveragePlugin @Inject constructor( override fun applyConfiguration(configuration: JSONObject) { try { if (configuration.has(rh.gs(R.string.key_absorption_maxtime))) sp.putDouble(R.string.key_absorption_maxtime, configuration.getDouble(rh.gs(R.string.key_absorption_maxtime))) - if (configuration.has(rh.gs(R.string.key_openapsama_autosens_period))) sp.putDouble(R.string.key_openapsama_autosens_period, configuration.getDouble(rh.gs(R.string.key_openapsama_autosens_period))) + if (configuration.has(rh.gs(R.string.key_openapsama_autosens_period))) sp.putDouble( + R.string.key_openapsama_autosens_period, + configuration.getDouble(rh.gs(R.string.key_openapsama_autosens_period)) + ) if (configuration.has(rh.gs(R.string.key_openapsama_autosens_max))) sp.getDouble(R.string.key_openapsama_autosens_max, configuration.getDouble(rh.gs(R.string.key_openapsama_autosens_max))) if (configuration.has(rh.gs(R.string.key_openapsama_autosens_min))) sp.getDouble(R.string.key_openapsama_autosens_min, configuration.getDouble(rh.gs(R.string.key_openapsama_autosens_min))) } catch (e: JSONException) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/NSClientSourcePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/NSClientSourcePlugin.kt index c54f90cdd2..0fd10c3c76 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/NSClientSourcePlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/NSClientSourcePlugin.kt @@ -9,24 +9,27 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.transactions.CgmSourceTransaction +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.BgSource -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.PluginBase -import info.nightscout.interfaces.PluginDescription -import info.nightscout.interfaces.PluginType import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.interfaces.XDripBroadcast -import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin -import info.nightscout.androidaps.plugins.general.nsclient.data.NSSgv import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification -import info.nightscout.interfaces.notifications.Notification +import info.nightscout.androidaps.plugins.sync.nsShared.StoreDataForDb +import info.nightscout.androidaps.plugins.sync.nsclient.data.NSSgv import info.nightscout.androidaps.receivers.DataWorkerStorage import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.T +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.PluginDescription +import info.nightscout.interfaces.PluginType +import info.nightscout.interfaces.notifications.Notification import info.nightscout.rx.bus.RxBus import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag +import info.nightscout.sdk.localmodel.entry.NSSgvV3 import info.nightscout.shared.sharedPreferences.SP +import org.json.JSONArray import org.json.JSONObject import javax.inject.Inject import javax.inject.Singleton @@ -39,12 +42,12 @@ class NSClientSourcePlugin @Inject constructor( config: Config ) : PluginBase( PluginDescription() - .mainType(PluginType.BGSOURCE) - .fragmentClass(BGSourceFragment::class.java.name) - .pluginIcon(R.drawable.ic_nsclient_bg) - .pluginName(R.string.nsclientbg) - .shortName(R.string.nsclientbgshort) - .description(R.string.description_source_ns_client), + .mainType(PluginType.BGSOURCE) + .fragmentClass(BGSourceFragment::class.java.name) + .pluginIcon(R.drawable.ic_nsclient_bg) + .pluginName(R.string.nsclientbg) + .shortName(R.string.nsclientbgshort) + .description(R.string.description_source_ns_client), aapsLogger, rh, injector ), BgSource { @@ -65,7 +68,7 @@ class NSClientSourcePlugin @Inject constructor( override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = false - private fun detectSource(glucoseValue: GlucoseValue) { + internal fun detectSource(glucoseValue: GlucoseValue) { if (glucoseValue.timestamp > lastBGTimeStamp) { isAdvancedFilteringEnabled = arrayOf( GlucoseValue.SourceSensor.DEXCOM_NATIVE_UNKNOWN, @@ -93,8 +96,8 @@ class NSClientSourcePlugin @Inject constructor( @Inject lateinit var dataWorkerStorage: DataWorkerStorage @Inject lateinit var repository: AppRepository @Inject lateinit var xDripBroadcast: XDripBroadcast - @Inject lateinit var dexcomPlugin: DexcomPlugin - @Inject lateinit var nsClientPlugin: NSClientPlugin + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var storeDataForDb: StoreDataForDb init { (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) @@ -113,52 +116,60 @@ class NSClientSourcePlugin @Inject constructor( ) } + private fun toGv(sgv: NSSgvV3): CgmSourceTransaction.TransactionGlucoseValue { + return CgmSourceTransaction.TransactionGlucoseValue( + timestamp = sgv.date, + value = sgv.sgv, + noise = sgv.noise?.toDouble(), + raw = sgv.filtered ?: sgv.sgv, + trendArrow = GlucoseValue.TrendArrow.fromString(sgv.direction.nsName), + nightscoutId = sgv.identifier, + sourceSensor = GlucoseValue.SourceSensor.fromString(sgv.device), + isValid = sgv.isValid + ) + } + @Suppress("SpellCheckingInspection") override fun doWork(): Result { var ret = Result.success() - val sgvs = dataWorkerStorage.pickupJSONArray(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) + val sgvs = dataWorkerStorage.pickupObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) ?: return Result.failure(workDataOf("Error" to "missing input data")) - xDripBroadcast.sendSgvs(sgvs) + if (!nsClientSourcePlugin.isEnabled() && !sp.getBoolean(R.string.key_ns_receive_cgm, false)) return Result.success(workDataOf("Result" to "Sync not enabled")) - try { - var latestDateInReceivedData: Long = 0 + var latestDateInReceivedData: Long = 0 + aapsLogger.debug(LTag.BGSOURCE, "Received NS Data: $sgvs") + val glucoseValues = mutableListOf() - aapsLogger.debug(LTag.BGSOURCE, "Received NS Data: $sgvs") - val glucoseValues = mutableListOf() - for (i in 0 until sgvs.length()) { - val sgv = toGv(sgvs.getJSONObject(i)) ?: continue - if (sgv.timestamp < dateUtil.now() && sgv.timestamp > latestDateInReceivedData) latestDateInReceivedData = sgv.timestamp - glucoseValues += sgv + try { + if (sgvs is JSONArray) { // V1 client + xDripBroadcast.sendSgvs(sgvs) + + for (i in 0 until sgvs.length()) { + val sgv = toGv(sgvs.getJSONObject(i)) ?: continue + if (sgv.timestamp < dateUtil.now() && sgv.timestamp > latestDateInReceivedData) latestDateInReceivedData = sgv.timestamp + glucoseValues += sgv + } + + } else if (sgvs is List<*>) { // V3 client +// xDripBroadcast.sendSgvs(sgvs) + + for (i in 0 until sgvs.size) { + val sgv = toGv(sgvs[i] as NSSgvV3) + if (sgv.timestamp < dateUtil.now() && sgv.timestamp > latestDateInReceivedData) latestDateInReceivedData = sgv.timestamp + glucoseValues += sgv + } } + activePlugin.activeNsClient?.updateLatestBgReceivedIfNewer(latestDateInReceivedData) // Was that sgv more less 5 mins ago ? if (T.msecs(dateUtil.now() - latestDateInReceivedData).mins() < 5L) { rxBus.send(EventDismissNotification(Notification.NS_ALARM)) rxBus.send(EventDismissNotification(Notification.NS_URGENT_ALARM)) } - nsClientPlugin.updateLatestDateReceivedIfNewer(latestDateInReceivedData) - - repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null, !nsClientSourcePlugin.isEnabled())) - .doOnError { - aapsLogger.error(LTag.DATABASE, "Error while saving values from NSClient App", it) - ret = Result.failure(workDataOf("Error" to it.toString())) - } - .blockingGet() - .also { result -> - result.updated.forEach { - xDripBroadcast.send(it) - nsClientSourcePlugin.detectSource(it) - aapsLogger.debug(LTag.DATABASE, "Updated bg $it") - } - result.inserted.forEach { - xDripBroadcast.send(it) - nsClientSourcePlugin.detectSource(it) - aapsLogger.debug(LTag.DATABASE, "Inserted bg $it") - } - } + storeDataForDb.glucoseValues.addAll(glucoseValues) } catch (e: Exception) { aapsLogger.error("Unhandled exception", e) ret = Result.failure(workDataOf("Error" to e.toString())) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt index d40347341f..2865dc34df 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt @@ -9,15 +9,15 @@ import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.transactions.CgmSourceTransaction import info.nightscout.androidaps.interfaces.BgSource -import info.nightscout.interfaces.BuildHelper import info.nightscout.androidaps.interfaces.PluginBase -import info.nightscout.interfaces.PluginDescription -import info.nightscout.interfaces.PluginType import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.interfaces.XDripBroadcast import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.extensions.isRunningTest +import info.nightscout.interfaces.BuildHelper +import info.nightscout.interfaces.PluginDescription +import info.nightscout.interfaces.PluginType import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag import info.nightscout.shared.sharedPreferences.SP diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/NSClientFragment.kt similarity index 67% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/NSClientFragment.kt index d86d3951bf..83823bf94b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/NSClientFragment.kt @@ -1,6 +1,8 @@ -package info.nightscout.androidaps.plugins.general.nsclient +package info.nightscout.androidaps.plugins.sync.nsShared import android.os.Bundle +import android.os.Handler +import android.os.HandlerThread import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater @@ -12,26 +14,30 @@ import androidx.core.view.MenuProvider import androidx.lifecycle.Lifecycle import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R -import info.nightscout.androidaps.database.entities.UserEntry.Action -import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.UserEntry import info.nightscout.androidaps.databinding.NsClientFragmentBinding +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.DataSyncSelector +import info.nightscout.androidaps.interfaces.NsClient +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.PluginFragment import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.logging.UserEntryLogger -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientUpdateGUI +import info.nightscout.androidaps.plugins.sync.nsclientV3.NSClientV3Plugin import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventNSClientRestart +import info.nightscout.rx.logging.AAPSLogger import info.nightscout.shared.sharedPreferences.SP import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject -class NSClientFragment : DaggerFragment(), MenuProvider { +class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment { - @Inject lateinit var nsClientPlugin: NSClientPlugin @Inject lateinit var sp: SP @Inject lateinit var rh: ResourceHelper @Inject lateinit var rxBus: RxBus @@ -39,6 +45,8 @@ class NSClientFragment : DaggerFragment(), MenuProvider { @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var dataSyncSelector: DataSyncSelector @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var activePlugin: ActivePlugin companion object { @@ -46,10 +54,17 @@ class NSClientFragment : DaggerFragment(), MenuProvider { const val ID_MENU_RESTART = 508 const val ID_MENU_SEND_NOW = 509 const val ID_MENU_FULL_SYNC = 510 + const val ID_MENU_TEST = 601 } + override var plugin: PluginBase? = null + private val nsClientPlugin + get() = activePlugin.activeNsClient + private val version: NsClient.Version get() = nsClientPlugin?.version ?: NsClient.Version.NONE + private val disposable = CompositeDisposable() + private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) private var _binding: NsClientFragmentBinding? = null // This property is only valid between onCreateView and @@ -65,22 +80,22 @@ class NSClientFragment : DaggerFragment(), MenuProvider { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.autoscroll.isChecked = nsClientPlugin.autoscroll + binding.autoscroll.isChecked = sp.getBoolean(R.string.key_nsclientinternal_autoscroll, true) binding.autoscroll.setOnCheckedChangeListener { _, isChecked -> sp.putBoolean(R.string.key_nsclientinternal_autoscroll, isChecked) - nsClientPlugin.autoscroll = isChecked updateGui() } - binding.paused.isChecked = nsClientPlugin.paused + binding.paused.isChecked = sp.getBoolean(R.string.key_nsclientinternal_paused, false) binding.paused.setOnCheckedChangeListener { _, isChecked -> - uel.log(if (isChecked) Action.NS_PAUSED else Action.NS_RESUME, Sources.NSClient) - nsClientPlugin.pause(isChecked) + uel.log(if (isChecked) UserEntry.Action.NS_PAUSED else UserEntry.Action.NS_RESUME, UserEntry.Sources.NSClient) + nsClientPlugin?.pause(isChecked) updateGui() } } override fun onCreateMenu(menu: Menu, inflater: MenuInflater) { + menu.add(Menu.FIRST, ID_MENU_TEST, 0, "Test").setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) menu.add(Menu.FIRST, ID_MENU_CLEAR_LOG, 0, rh.gs(R.string.clearlog)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) menu.add(Menu.FIRST, ID_MENU_RESTART, 0, rh.gs(R.string.restart)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) menu.add(Menu.FIRST, ID_MENU_SEND_NOW, 0, rh.gs(R.string.deliver_now)).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) @@ -91,7 +106,7 @@ class NSClientFragment : DaggerFragment(), MenuProvider { override fun onMenuItemSelected(item: MenuItem): Boolean = when (item.itemId) { ID_MENU_CLEAR_LOG -> { - nsClientPlugin.clearLog() + nsClientPlugin?.clearLog() true } @@ -101,7 +116,7 @@ class NSClientFragment : DaggerFragment(), MenuProvider { } ID_MENU_SEND_NOW -> { - nsClientPlugin.resend("GUI") + nsClientPlugin?.resend("GUI") true } @@ -109,12 +124,17 @@ class NSClientFragment : DaggerFragment(), MenuProvider { context?.let { context -> OKDialog.showConfirmation( context, rh.gs(R.string.nsclientinternal), rh.gs(R.string.full_sync_comment), - Runnable { dataSyncSelector.resetToNextFullSync() } + Runnable { nsClientPlugin?.resetToFullSync() } ) } true } + ID_MENU_TEST -> { + nsClientPlugin?.let { plugin -> if (plugin is NSClientV3Plugin) handler.post { plugin.test() } } + true + } + else -> false } @@ -134,12 +154,11 @@ class NSClientFragment : DaggerFragment(), MenuProvider { private fun updateGui() { if (_binding == null) return - nsClientPlugin.updateLog() binding.paused.isChecked = sp.getBoolean(R.string.key_nsclientinternal_paused, false) - binding.log.text = nsClientPlugin.textLog - if (nsClientPlugin.autoscroll) binding.logScrollview.fullScroll(ScrollView.FOCUS_DOWN) - binding.url.text = nsClientPlugin.url() - binding.status.text = nsClientPlugin.status + binding.log.text = nsClientPlugin?.textLog() + if (sp.getBoolean(R.string.key_nsclientinternal_autoscroll, true)) binding.logScrollview.fullScroll(ScrollView.FOCUS_DOWN) + binding.url.text = nsClientPlugin?.address + binding.status.text = nsClientPlugin?.status val size = dataSyncSelector.queueSize() binding.queue.text = if (size >= 0) size.toString() else rh.gs(R.string.value_unavailable_short) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/StoreDataForDb.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/StoreDataForDb.kt new file mode 100644 index 0000000000..3028832d48 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/StoreDataForDb.kt @@ -0,0 +1,719 @@ +package info.nightscout.androidaps.plugins.sync.nsShared + +import android.content.Context +import android.os.SystemClock +import androidx.work.Worker +import androidx.work.WorkerParameters +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.R +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.Bolus +import info.nightscout.androidaps.database.entities.BolusCalculatorResult +import info.nightscout.androidaps.database.entities.Carbs +import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch +import info.nightscout.androidaps.database.entities.ExtendedBolus +import info.nightscout.androidaps.database.entities.GlucoseValue +import info.nightscout.androidaps.database.entities.OfflineEvent +import info.nightscout.androidaps.database.entities.ProfileSwitch +import info.nightscout.androidaps.database.entities.TemporaryBasal +import info.nightscout.androidaps.database.entities.TemporaryTarget +import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.database.entities.UserEntry +import info.nightscout.androidaps.database.entities.ValueWithUnit +import info.nightscout.androidaps.database.transactions.CgmSourceTransaction +import info.nightscout.androidaps.database.transactions.SyncNsBolusCalculatorResultTransaction +import info.nightscout.androidaps.database.transactions.SyncNsBolusTransaction +import info.nightscout.androidaps.database.transactions.SyncNsCarbsTransaction +import info.nightscout.androidaps.database.transactions.SyncNsEffectiveProfileSwitchTransaction +import info.nightscout.androidaps.database.transactions.SyncNsExtendedBolusTransaction +import info.nightscout.androidaps.database.transactions.SyncNsOfflineEventTransaction +import info.nightscout.androidaps.database.transactions.SyncNsProfileSwitchTransaction +import info.nightscout.androidaps.database.transactions.SyncNsTemporaryBasalTransaction +import info.nightscout.androidaps.database.transactions.SyncNsTemporaryTargetTransaction +import info.nightscout.androidaps.database.transactions.SyncNsTherapyEventTransaction +import info.nightscout.androidaps.database.transactions.UserEntryTransaction +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.NsClient +import info.nightscout.androidaps.interfaces.XDripBroadcast +import info.nightscout.androidaps.logging.UserEntryLogger +import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification +import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin +import info.nightscout.androidaps.plugins.source.NSClientSourcePlugin +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientNewLog +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.Constants +import info.nightscout.interfaces.notifications.Notification +import info.nightscout.rx.bus.RxBus +import info.nightscout.rx.logging.AAPSLogger +import info.nightscout.rx.logging.LTag +import info.nightscout.sdk.localmodel.treatment.NSBolus +import info.nightscout.sdk.localmodel.treatment.NSBolusWizard +import info.nightscout.sdk.localmodel.treatment.NSCarbs +import info.nightscout.sdk.localmodel.treatment.NSEffectiveProfileSwitch +import info.nightscout.sdk.localmodel.treatment.NSExtendedBolus +import info.nightscout.sdk.localmodel.treatment.NSOfflineEvent +import info.nightscout.sdk.localmodel.treatment.NSProfileSwitch +import info.nightscout.sdk.localmodel.treatment.NSTemporaryBasal +import info.nightscout.sdk.localmodel.treatment.NSTemporaryTarget +import info.nightscout.sdk.localmodel.treatment.NSTherapyEvent +import info.nightscout.shared.sharedPreferences.SP +import java.util.concurrent.TimeUnit +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class StoreDataForDb @Inject constructor( + private val aapsLogger: AAPSLogger, + private val rxBus: RxBus, + private val repository: AppRepository, + private val sp: SP, + private val uel: UserEntryLogger, + private val dateUtil: DateUtil, + private val activePlugin: ActivePlugin, + private val config: Config, + private val nsClientSourcePlugin: NSClientSourcePlugin, + private val xDripBroadcast: XDripBroadcast, + private val virtualPumpPlugin: VirtualPumpPlugin +) { + + val glucoseValues: MutableList = mutableListOf() + + val boluses: MutableList = mutableListOf() + val carbs: MutableList = mutableListOf() + val temporaryTargets: MutableList = mutableListOf() + val effectiveProfileSwitches: MutableList = mutableListOf() + val bolusCalculatorResults: MutableList = mutableListOf() + val therapyEvents: MutableList = mutableListOf() + val extendedBoluses: MutableList = mutableListOf() + val temporaryBasals: MutableList = mutableListOf() + val profileSwitches: MutableList = mutableListOf() + val offlineEvents: MutableList = mutableListOf() + + private val userEntries: MutableList = mutableListOf() + + private val inserted = HashMap() + private val updated = HashMap() + private val invalidated = HashMap() + private val nsIdUpdated = HashMap() + private val durationUpdated = HashMap() + private val ended = HashMap() + + private val pause = 1000L // to slow down db operations + + class StoreBgWorker( + context: Context, + params: WorkerParameters + ) : Worker(context, params) { + + @Inject lateinit var storeDataForDb: StoreDataForDb + + override fun doWork(): Result { + storeDataForDb.storeGlucoseValuesToDb() + return Result.success() + } + + init { + (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) + } + } + + fun HashMap.inc(key: T) = + if (containsKey(key)) merge(key, 1, Long::plus) + else put(key, 1) + + private fun storeGlucoseValuesToDb() { + rxBus.send(EventNSClientNewLog("PROCESSING BG", "", activePlugin.activeNsClient?.version ?: NsClient.Version.V3)) + + if (glucoseValues.isNotEmpty()) + repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving values from NSClient App", it) + } + .blockingGet() + .also { result -> + glucoseValues.clear() + result.updated.forEach { + xDripBroadcast.send(it) + nsClientSourcePlugin.detectSource(it) + aapsLogger.debug(LTag.DATABASE, "Updated bg $it") + updated.inc(GlucoseValue::class.java.simpleName) + } + result.inserted.forEach { + xDripBroadcast.send(it) + nsClientSourcePlugin.detectSource(it) + aapsLogger.debug(LTag.DATABASE, "Inserted bg $it") + inserted.inc(GlucoseValue::class.java.simpleName) + } + result.updatedNsId.forEach { + xDripBroadcast.send(it) + nsClientSourcePlugin.detectSource(it) + aapsLogger.debug(LTag.DATABASE, "Updated nsId bg $it") + nsIdUpdated.inc(GlucoseValue::class.java.simpleName) + } + } + + sendLog("GlucoseValue", GlucoseValue::class.java.simpleName) + SystemClock.sleep(pause) + rxBus.send(EventNSClientNewLog("DONE BG", "", activePlugin.activeNsClient?.version ?: NsClient.Version.V3)) + } + + fun storeTreatmentsToDb() { + rxBus.send(EventNSClientNewLog("PROCESSING TR", "", activePlugin.activeNsClient?.version ?: NsClient.Version.V3)) + + if (boluses.isNotEmpty()) + repository.runTransactionForResult(SyncNsBolusTransaction(boluses)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving bolus", it) + } + .blockingGet() + .also { result -> + boluses.clear() + result.inserted.forEach { + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.BOLUS, UserEntry.Sources.NSClient, it.notes ?: "", + listOf(ValueWithUnit.Timestamp(it.timestamp), ValueWithUnit.Insulin(it.amount)) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Inserted bolus $it") + inserted.inc(NSBolus::class.java.simpleName) + } + result.invalidated.forEach { + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.BOLUS_REMOVED, UserEntry.Sources.NSClient, "", + listOf(ValueWithUnit.Timestamp(it.timestamp), ValueWithUnit.Insulin(it.amount)) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it") + invalidated.inc(NSBolus::class.java.simpleName) + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId of bolus $it") + nsIdUpdated.inc(NSBolus::class.java.simpleName) + } + result.updated.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated amount of bolus $it") + updated.inc(NSBolus::class.java.simpleName) + } + } + + sendLog("Bolus", NSBolus::class.java.simpleName) + SystemClock.sleep(pause) + + if (carbs.isNotEmpty()) + repository.runTransactionForResult(SyncNsCarbsTransaction(carbs)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving carbs", it) + } + .blockingGet() + .also { result -> + carbs.clear() + result.inserted.forEach { + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.CARBS, UserEntry.Sources.NSClient, it.notes ?: "", + listOf(ValueWithUnit.Timestamp(it.timestamp), ValueWithUnit.Gram(it.amount.toInt())) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Inserted carbs $it") + inserted.inc(NSCarbs::class.java.simpleName) + } + result.invalidated.forEach { + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.CARBS_REMOVED, UserEntry.Sources.NSClient, "", + listOf(ValueWithUnit.Timestamp(it.timestamp), ValueWithUnit.Gram(it.amount.toInt())) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") + invalidated.inc(NSCarbs::class.java.simpleName) + } + result.updated.forEach { + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.CARBS, UserEntry.Sources.NSClient, it.notes ?: "", + listOf(ValueWithUnit.Timestamp(it.timestamp), ValueWithUnit.Gram(it.amount.toInt())) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Updated carbs $it") + updated.inc(NSCarbs::class.java.simpleName) + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId carbs $it") + nsIdUpdated.inc(NSCarbs::class.java.simpleName) + } + + } + + sendLog("Carbs", NSCarbs::class.java.simpleName) + SystemClock.sleep(pause) + + if (temporaryTargets.isNotEmpty()) + repository.runTransactionForResult(SyncNsTemporaryTargetTransaction(temporaryTargets)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) + } + .blockingGet() + .also { result -> + temporaryTargets.clear() + result.inserted.forEach { tt -> + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.TT, UserEntry.Sources.NSClient, "", + listOf( + ValueWithUnit.TherapyEventTTReason(tt.reason), + ValueWithUnit.fromGlucoseUnit(tt.lowTarget, Constants.MGDL), + ValueWithUnit.fromGlucoseUnit(tt.highTarget, Constants.MGDL).takeIf { tt.lowTarget != tt.highTarget }, + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tt.duration).toInt()) + ) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Inserted TemporaryTarget $tt") + inserted.inc(NSTemporaryTarget::class.java.simpleName) + } + result.invalidated.forEach { tt -> + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.TT_REMOVED, UserEntry.Sources.NSClient, "", + listOf( + ValueWithUnit.TherapyEventTTReason(tt.reason), + ValueWithUnit.Mgdl(tt.lowTarget), + ValueWithUnit.Mgdl(tt.highTarget).takeIf { tt.lowTarget != tt.highTarget }, + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tt.duration).toInt()) + ) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Invalidated TemporaryTarget $tt") + invalidated.inc(NSTemporaryTarget::class.java.simpleName) + } + result.ended.forEach { tt -> + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.CANCEL_TT, UserEntry.Sources.NSClient, "", + listOf( + ValueWithUnit.TherapyEventTTReason(tt.reason), + ValueWithUnit.Mgdl(tt.lowTarget), + ValueWithUnit.Mgdl(tt.highTarget).takeIf { tt.lowTarget != tt.highTarget }, + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tt.duration).toInt()) + ) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Updated TemporaryTarget $tt") + ended.inc(NSTemporaryTarget::class.java.simpleName) + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId TemporaryTarget $it") + nsIdUpdated.inc(NSTemporaryTarget::class.java.simpleName) + } + result.updatedDuration.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated duration TemporaryTarget $it") + durationUpdated.inc(NSTemporaryTarget::class.java.simpleName) + } + } + + sendLog("TemporaryTarget", NSTemporaryTarget::class.java.simpleName) + SystemClock.sleep(pause) + + if (temporaryBasals.isNotEmpty()) + repository.runTransactionForResult(SyncNsTemporaryBasalTransaction(temporaryBasals)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving temporary basal", it) + } + .blockingGet() + .also { result -> + temporaryBasals.clear() + result.inserted.forEach { + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.TEMP_BASAL, UserEntry.Sources.NSClient, "", + listOf( + ValueWithUnit.Timestamp(it.timestamp), + if (it.isAbsolute) ValueWithUnit.UnitPerHour(it.rate) else ValueWithUnit.Percent(it.rate.toInt()), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) + ) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Inserted TemporaryBasal $it") + inserted.inc(NSTemporaryBasal::class.java.simpleName) + } + result.invalidated.forEach { + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.TEMP_BASAL_REMOVED, UserEntry.Sources.NSClient, "", + listOf( + ValueWithUnit.Timestamp(it.timestamp), + if (it.isAbsolute) ValueWithUnit.UnitPerHour(it.rate) else ValueWithUnit.Percent(it.rate.toInt()), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) + ) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Invalidated TemporaryBasal $it") + invalidated.inc(NSTemporaryBasal::class.java.simpleName) + } + result.ended.forEach { + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.CANCEL_TEMP_BASAL, UserEntry.Sources.NSClient, "", + listOf( + ValueWithUnit.Timestamp(it.timestamp), + if (it.isAbsolute) ValueWithUnit.UnitPerHour(it.rate) else ValueWithUnit.Percent(it.rate.toInt()), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) + ) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Ended TemporaryBasal $it") + ended.inc(NSTemporaryBasal::class.java.simpleName) + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId TemporaryBasal $it") + nsIdUpdated.inc(NSTemporaryBasal::class.java.simpleName) + } + result.updatedDuration.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated duration TemporaryBasal $it") + durationUpdated.inc(NSTemporaryBasal::class.java.simpleName) + } + } + + sendLog("TemporaryBasal", NSTemporaryBasal::class.java.simpleName) + SystemClock.sleep(pause) + + if (effectiveProfileSwitches.isNotEmpty()) + repository.runTransactionForResult(SyncNsEffectiveProfileSwitchTransaction(effectiveProfileSwitches)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving EffectiveProfileSwitch", it) + } + .blockingGet() + .also { result -> + effectiveProfileSwitches.clear() + result.inserted.forEach { + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.PROFILE_SWITCH, UserEntry.Sources.NSClient, "", + listOf(ValueWithUnit.Timestamp(it.timestamp)) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Inserted EffectiveProfileSwitch $it") + inserted.inc(NSEffectiveProfileSwitch::class.java.simpleName) + } + result.invalidated.forEach { + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.PROFILE_SWITCH_REMOVED, UserEntry.Sources.NSClient, "", + listOf(ValueWithUnit.Timestamp(it.timestamp)) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Invalidated EffectiveProfileSwitch $it") + invalidated.inc(NSEffectiveProfileSwitch::class.java.simpleName) + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId EffectiveProfileSwitch $it") + nsIdUpdated.inc(NSEffectiveProfileSwitch::class.java.simpleName) + } + } + + sendLog("EffectiveProfileSwitch", NSEffectiveProfileSwitch::class.java.simpleName) + SystemClock.sleep(pause) + + if (profileSwitches.isNotEmpty()) + repository.runTransactionForResult(SyncNsProfileSwitchTransaction(profileSwitches)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving ProfileSwitch", it) + } + .blockingGet() + .also { result -> + profileSwitches.clear() + result.inserted.forEach { + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.PROFILE_SWITCH, UserEntry.Sources.NSClient, "", + listOf(ValueWithUnit.Timestamp(it.timestamp)) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Inserted ProfileSwitch $it") + inserted.inc(NSProfileSwitch::class.java.simpleName) + } + result.invalidated.forEach { + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.PROFILE_SWITCH_REMOVED, UserEntry.Sources.NSClient, "", + listOf(ValueWithUnit.Timestamp(it.timestamp)) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Invalidated ProfileSwitch $it") + invalidated.inc(NSProfileSwitch::class.java.simpleName) + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId ProfileSwitch $it") + nsIdUpdated.inc(NSProfileSwitch::class.java.simpleName) + } + } + + sendLog("ProfileSwitch", NSProfileSwitch::class.java.simpleName) + SystemClock.sleep(pause) + + if (bolusCalculatorResults.isNotEmpty()) + repository.runTransactionForResult(SyncNsBolusCalculatorResultTransaction(bolusCalculatorResults)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving BolusCalculatorResult", it) + } + .blockingGet() + .also { result -> + bolusCalculatorResults.clear() + result.inserted.forEach { + aapsLogger.debug(LTag.DATABASE, "Inserted BolusCalculatorResult $it") + inserted.inc(NSBolusWizard::class.java.simpleName) + } + result.invalidated.forEach { + aapsLogger.debug(LTag.DATABASE, "Invalidated BolusCalculatorResult $it") + invalidated.inc(NSBolusWizard::class.java.simpleName) + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId BolusCalculatorResult $it") + nsIdUpdated.inc(NSBolusWizard::class.java.simpleName) + } + } + + sendLog("BolusCalculatorResult", NSBolusWizard::class.java.simpleName) + SystemClock.sleep(pause) + + if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) + therapyEvents.filter { it.type == TherapyEvent.Type.ANNOUNCEMENT }.forEach { + if (it.timestamp > dateUtil.now() - 15 * 60 * 1000L && + it.note?.isNotEmpty() == true && + it.enteredBy != sp.getString("careportal_enteredby", "AndroidAPS") + ) { + if (sp.getBoolean(R.string.key_ns_announcements, config.NSCLIENT)) + rxBus.send(EventNewNotification(Notification(Notification.NS_ANNOUNCEMENT, it.note ?: "", Notification.ANNOUNCEMENT, 60))) + } + } + if (therapyEvents.isNotEmpty()) + repository.runTransactionForResult(SyncNsTherapyEventTransaction(therapyEvents)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) + } + .blockingGet() + .also { result -> + therapyEvents.clear() + result.inserted.forEach { therapyEvent -> + val action = when (therapyEvent.type) { + TherapyEvent.Type.CANNULA_CHANGE -> UserEntry.Action.SITE_CHANGE + TherapyEvent.Type.INSULIN_CHANGE -> UserEntry.Action.RESERVOIR_CHANGE + else -> UserEntry.Action.CAREPORTAL + } + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + action, UserEntry.Sources.NSClient, therapyEvent.note ?: "", + listOf(ValueWithUnit.Timestamp(therapyEvent.timestamp), + ValueWithUnit.TherapyEventType(therapyEvent.type), + ValueWithUnit.fromGlucoseUnit(therapyEvent.glucose ?: 0.0, therapyEvent.glucoseUnit.toString).takeIf { therapyEvent.glucose != null }) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Inserted TherapyEvent $therapyEvent") + inserted.inc(NSTherapyEvent::class.java.simpleName) + } + result.invalidated.forEach { therapyEvent -> + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.CAREPORTAL_REMOVED, UserEntry.Sources.NSClient, therapyEvent.note ?: "", + listOf(ValueWithUnit.Timestamp(therapyEvent.timestamp), + ValueWithUnit.TherapyEventType(therapyEvent.type), + ValueWithUnit.fromGlucoseUnit(therapyEvent.glucose ?: 0.0, therapyEvent.glucoseUnit.toString).takeIf { therapyEvent.glucose != null }) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Invalidated TherapyEvent $therapyEvent") + invalidated.inc(NSTherapyEvent::class.java.simpleName) + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId TherapyEvent $it") + nsIdUpdated.inc(NSTherapyEvent::class.java.simpleName) + } + result.updatedDuration.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId TherapyEvent $it") + durationUpdated.inc(NSTherapyEvent::class.java.simpleName) + } + } + + sendLog("TherapyEvent", NSTherapyEvent::class.java.simpleName) + SystemClock.sleep(pause) + + if (offlineEvents.isNotEmpty()) + repository.runTransactionForResult(SyncNsOfflineEventTransaction(offlineEvents)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + } + .blockingGet() + .also { result -> + result.inserted.forEach { oe -> + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.LOOP_CHANGE, UserEntry.Sources.NSClient, "", + listOf( + ValueWithUnit.OfflineEventReason(oe.reason), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt()) + ) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $oe") + inserted.inc(NSOfflineEvent::class.java.simpleName) + } + result.invalidated.forEach { oe -> + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.LOOP_REMOVED, UserEntry.Sources.NSClient, "", + listOf( + ValueWithUnit.OfflineEventReason(oe.reason), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt()) + ) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Invalidated OfflineEvent $oe") + invalidated.inc(NSOfflineEvent::class.java.simpleName) + } + result.ended.forEach { oe -> + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.LOOP_CHANGE, UserEntry.Sources.NSClient, "", + listOf( + ValueWithUnit.OfflineEventReason(oe.reason), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt()) + ) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $oe") + ended.inc(NSOfflineEvent::class.java.simpleName) + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId OfflineEvent $it") + nsIdUpdated.inc(NSOfflineEvent::class.java.simpleName) + } + result.updatedDuration.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated duration OfflineEvent $it") + durationUpdated.inc(NSOfflineEvent::class.java.simpleName) + } + } + + sendLog("OfflineEvent", NSOfflineEvent::class.java.simpleName) + SystemClock.sleep(pause) + + if (extendedBoluses.isNotEmpty()) + repository.runTransactionForResult(SyncNsExtendedBolusTransaction(extendedBoluses)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving extended bolus", it) + } + .blockingGet() + .also { result -> + result.inserted.forEach { + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.EXTENDED_BOLUS, UserEntry.Sources.NSClient, "", + listOf( + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.Insulin(it.amount), + ValueWithUnit.UnitPerHour(it.rate), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) + ) + ) + ) + if (it.isEmulatingTempBasal) virtualPumpPlugin.fakeDataDetected = true + aapsLogger.debug(LTag.DATABASE, "Inserted ExtendedBolus $it") + inserted.inc(NSExtendedBolus::class.java.simpleName) + } + result.invalidated.forEach { + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.EXTENDED_BOLUS_REMOVED, UserEntry.Sources.NSClient, "", + listOf( + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.Insulin(it.amount), + ValueWithUnit.UnitPerHour(it.rate), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) + ) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Invalidated ExtendedBolus $it") + invalidated.inc(NSExtendedBolus::class.java.simpleName) + } + result.ended.forEach { + if (config.NSCLIENT.not()) userEntries.add( + UserEntryTransaction.Entry( + dateUtil.now(), + UserEntry.Action.CANCEL_EXTENDED_BOLUS, UserEntry.Sources.NSClient, "", + listOf( + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.Insulin(it.amount), + ValueWithUnit.UnitPerHour(it.rate), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) + ) + ) + ) + aapsLogger.debug(LTag.DATABASE, "Updated ExtendedBolus $it") + ended.inc(NSExtendedBolus::class.java.simpleName) + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId ExtendedBolus $it") + nsIdUpdated.inc(NSExtendedBolus::class.java.simpleName) + } + result.updatedDuration.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated duration ExtendedBolus $it") + durationUpdated.inc(NSExtendedBolus::class.java.simpleName) + } + } + + sendLog("ExtendedBolus", NSExtendedBolus::class.java.simpleName) + SystemClock.sleep(pause) + + uel.log(userEntries) + rxBus.send(EventNSClientNewLog("DONE TR", "", activePlugin.activeNsClient?.version ?: NsClient.Version.V3)) + } + + private fun sendLog(item: String, clazz: String) { + inserted[clazz]?.let { + rxBus.send(EventNSClientNewLog("INSERT", "$item $it", activePlugin.activeNsClient?.version ?: NsClient.Version.V3)) + } + inserted.remove(clazz) + updated[clazz]?.let { + rxBus.send(EventNSClientNewLog("UPDATE", "$item $it", activePlugin.activeNsClient?.version ?: NsClient.Version.V3)) + } + updated.remove(clazz) + invalidated[clazz]?.let { + rxBus.send(EventNSClientNewLog("INVALIDATE", "$item $it", activePlugin.activeNsClient?.version ?: NsClient.Version.V3)) + } + invalidated.remove(clazz) + nsIdUpdated[clazz]?.let { + rxBus.send(EventNSClientNewLog("NS_ID", "$item $it", activePlugin.activeNsClient?.version ?: NsClient.Version.V3)) + } + nsIdUpdated.remove(clazz) + durationUpdated[clazz]?.let { + rxBus.send(EventNSClientNewLog("DURATION", "$item $it", activePlugin.activeNsClient?.version ?: NsClient.Version.V3)) + } + durationUpdated.remove(clazz) + ended[clazz]?.let { + rxBus.send(EventNSClientNewLog("CUT", "$item $it", activePlugin.activeNsClient?.version ?: NsClient.Version.V3)) + } + ended.remove(clazz) + } +} \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/events/EventNSClientNewLog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/events/EventNSClientNewLog.kt similarity index 71% rename from core/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/events/EventNSClientNewLog.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/events/EventNSClientNewLog.kt index db499d6296..ecef80a645 100644 --- a/core/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/events/EventNSClientNewLog.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/events/EventNSClientNewLog.kt @@ -1,10 +1,11 @@ -package info.nightscout.androidaps.plugins.general.nsclient.events +package info.nightscout.androidaps.plugins.sync.nsShared.events +import info.nightscout.androidaps.interfaces.NsClient import info.nightscout.rx.events.Event import java.text.SimpleDateFormat import java.util.Locale -class EventNSClientNewLog(var action: String, var logText: String) : Event() { +class EventNSClientNewLog(val action: String, val logText: String, val version: NsClient.Version) : Event() { var date = System.currentTimeMillis() private var timeFormat = SimpleDateFormat("HH:mm:ss", Locale.getDefault()) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/events/EventNSClientResend.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/events/EventNSClientResend.kt similarity index 58% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/events/EventNSClientResend.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/events/EventNSClientResend.kt index 51b2528229..5c34cf5b68 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/events/EventNSClientResend.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/events/EventNSClientResend.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient.events +package info.nightscout.androidaps.plugins.sync.nsShared.events import info.nightscout.rx.events.Event diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/events/EventNSClientStatus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/events/EventNSClientStatus.kt new file mode 100644 index 0000000000..e83c862a99 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/events/EventNSClientStatus.kt @@ -0,0 +1,9 @@ +package info.nightscout.androidaps.plugins.sync.nsShared.events + +import info.nightscout.androidaps.events.EventStatus +import info.nightscout.androidaps.interfaces.NsClient +import info.nightscout.androidaps.interfaces.ResourceHelper + +class EventNSClientStatus(var text: String, val version: NsClient.Version) : EventStatus() { + override fun getStatus(rh: ResourceHelper): String = text +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/events/EventNSClientUpdateGUI.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/events/EventNSClientUpdateGUI.kt new file mode 100644 index 0000000000..af08d1acaa --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsShared/events/EventNSClientUpdateGUI.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.sync.nsShared.events + +import info.nightscout.rx.events.EventUpdateGui + +class EventNSClientUpdateGUI : EventUpdateGui() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/DataSyncSelectorImplementation.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/DataSyncSelectorImplementation.kt similarity index 69% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/DataSyncSelectorImplementation.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/DataSyncSelectorImplementation.kt index 8ef4968ab8..0fe20d456f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/DataSyncSelectorImplementation.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/DataSyncSelectorImplementation.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient +package info.nightscout.androidaps.plugins.sync.nsclient import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository @@ -20,7 +20,9 @@ import info.nightscout.androidaps.extensions.toJson import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.DataSyncSelector import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.plugins.sync.nsclient.extensions.toJson import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.extensions.toJson import info.nightscout.plugins.profile.ProfilePlugin import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag @@ -34,7 +36,6 @@ class DataSyncSelectorImplementation @Inject constructor( private val aapsLogger: AAPSLogger, private val dateUtil: DateUtil, private val profileFunction: ProfileFunction, - private val nsClientPlugin: NSClientPlugin, private val activePlugin: ActivePlugin, private val appRepository: AppRepository, private val profilePlugin: ProfilePlugin @@ -96,30 +97,10 @@ class DataSyncSelectorImplementation @Inject constructor( } override fun resetToNextFullSync() { - appRepository.getLastGlucoseValueIdWrapped().blockingGet().run { - val currentLast = if (this is ValueWrapper.Existing) this.value else 0L - sp.putLong(R.string.key_ns_glucose_value_new_data_id, currentLast) - } sp.remove(R.string.key_ns_glucose_value_last_synced_id) - - appRepository.getLastTemporaryBasalIdWrapped().blockingGet().run { - val currentLast = if (this is ValueWrapper.Existing) this.value else 0L - sp.putLong(R.string.key_ns_temporary_basal_new_data_id, currentLast) - } sp.remove(R.string.key_ns_temporary_basal_last_synced_id) - - appRepository.getLastTempTargetIdWrapped().blockingGet().run { - val currentLast = if (this is ValueWrapper.Existing) this.value else 0L - sp.putLong(R.string.key_ns_temporary_target_new_data_id, currentLast) - } sp.remove(R.string.key_ns_temporary_target_last_synced_id) - - appRepository.getLastExtendedBolusIdWrapped().blockingGet().run { - val currentLast = if (this is ValueWrapper.Existing) this.value else 0L - sp.putLong(R.string.key_ns_extended_bolus_new_data_id, currentLast) - } 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) @@ -137,7 +118,7 @@ class DataSyncSelectorImplementation @Inject constructor( 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") + //aapsLogger.debug(LTag.NSCLIENT, "Setting Bolus data sync from $lastSynced") sp.putLong(R.string.key_ns_bolus_last_synced_id, lastSynced) } } @@ -153,9 +134,7 @@ class DataSyncSelectorImplementation @Inject constructor( } } - //private var lastBolusId = -1L - //private var lastBolusTime = -1L - override fun processChangedBolusesCompat(): Boolean { + override tailrec fun processChangedBolusesCompat() { val lastDbIdWrapped = appRepository.getLastBolusIdWrapped().blockingGet() val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L var startId = sp.getLong(R.string.key_ns_bolus_last_synced_id, 0) @@ -163,27 +142,35 @@ class DataSyncSelectorImplementation @Inject constructor( sp.putLong(R.string.key_ns_bolus_last_synced_id, 0) startId = 0 } - //if (startId == lastBolusId && dateUtil.now() - lastBolusTime < 5000) return false - //lastBolusId = startId - //lastBolusTime = dateUtil.now() queueCounter.bolusesRemaining = lastDbId - startId appRepository.getNextSyncElementBolus(startId).blockingGet()?.let { bolus -> aapsLogger.info(LTag.NSCLIENT, "Loading Bolus data Start: $startId ID: ${bolus.first.id} HistoryID: ${bolus.second.id} ") when { - // only NsId changed, no need to upload - bolus.first.onlyNsIdAdded(bolus.second) -> { + // 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.first.id} HistoryID: ${bolus.second.id} ") confirmLastBolusIdIfGreater(bolus.second.id) - //lastBolusId = -1 processChangedBolusesCompat() + return + } + // only NsId changed, no need to upload + bolus.first.onlyNsIdAdded(bolus.second) -> { aapsLogger.info(LTag.NSCLIENT, "Ignoring Bolus. Only NS id changed ID: ${bolus.first.id} HistoryID: ${bolus.second.id} ") - return false + confirmLastBolusIdIfGreater(bolus.second.id) + processChangedBolusesCompat() + return } // without nsId = create new - bolus.first.interfaceIDs.nightscoutId == null -> - nsClientPlugin.nsClientService?.dbAdd("treatments", bolus.first.toJson(true, dateUtil), DataSyncSelector.PairBolus(bolus.first, bolus.second.id), "$startId/$lastDbId") - // with nsId = update - bolus.first.interfaceIDs.nightscoutId != null -> - nsClientPlugin.nsClientService?.dbUpdate( + bolus.first.interfaceIDs.nightscoutId == null -> + activePlugin.activeNsClient?.nsClientService?.dbAdd( + "treatments", + bolus.first.toJson(true, dateUtil), + 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?.nsClientService?.dbUpdate( "treatments", bolus.first.interfaceIDs.nightscoutId, bolus.first.toJson(false, dateUtil), @@ -191,14 +178,13 @@ class DataSyncSelectorImplementation @Inject constructor( "$startId/$lastDbId" ) } - return true + return } - return false } 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") + //aapsLogger.debug(LTag.NSCLIENT, "Setting Carbs data sync from $lastSynced") sp.putLong(R.string.key_ns_carbs_last_synced_id, lastSynced) } } @@ -211,9 +197,7 @@ class DataSyncSelectorImplementation @Inject constructor( } } - //private var lastCarbsId = -1L - //private var lastCarbsTime = -1L - override fun processChangedCarbsCompat(): Boolean { + override tailrec fun processChangedCarbsCompat() { val lastDbIdWrapped = appRepository.getLastCarbsIdWrapped().blockingGet() val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L var startId = sp.getLong(R.string.key_ns_carbs_last_synced_id, 0) @@ -221,27 +205,30 @@ class DataSyncSelectorImplementation @Inject constructor( sp.putLong(R.string.key_ns_carbs_last_synced_id, 0) startId = 0 } - //if (startId == lastCarbsId && dateUtil.now() - lastCarbsTime < 5000) return false - //lastCarbsId = startId - //lastCarbsTime = dateUtil.now() queueCounter.carbsRemaining = lastDbId - startId appRepository.getNextSyncElementCarbs(startId).blockingGet()?.let { carb -> aapsLogger.info(LTag.NSCLIENT, "Loading Carbs data Start: $startId ID: ${carb.first.id} HistoryID: ${carb.second.id} ") when { - // only NsId changed, no need to upload - carb.first.onlyNsIdAdded(carb.second) -> { + // 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.first.id} HistoryID: ${carb.second.id} ") confirmLastCarbsIdIfGreater(carb.second.id) - //lastCarbsId = -1 processChangedCarbsCompat() + 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.first.id} HistoryID: ${carb.second.id} ") - return false + confirmLastCarbsIdIfGreater(carb.second.id) + processChangedCarbsCompat() + return } // without nsId = create new - carb.first.interfaceIDs.nightscoutId == null -> - nsClientPlugin.nsClientService?.dbAdd("treatments", carb.first.toJson(true, dateUtil), DataSyncSelector.PairCarbs(carb.first, carb.second.id), "$startId/$lastDbId") - // with nsId = update - carb.first.interfaceIDs.nightscoutId != null -> - nsClientPlugin.nsClientService?.dbUpdate( + carb.first.interfaceIDs.nightscoutId == null -> + activePlugin.activeNsClient?.nsClientService?.dbAdd("treatments", carb.first.toJson(true, dateUtil), 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?.nsClientService?.dbUpdate( "treatments", carb.first.interfaceIDs.nightscoutId, carb.first.toJson(false, dateUtil), @@ -249,14 +236,13 @@ class DataSyncSelectorImplementation @Inject constructor( "$startId/$lastDbId" ) } - return true + return } - return false } 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") + //aapsLogger.debug(LTag.NSCLIENT, "Setting BolusCalculatorResult data sync from $lastSynced") sp.putLong(R.string.key_ns_bolus_calculator_result_last_synced_id, lastSynced) } } @@ -269,9 +255,7 @@ class DataSyncSelectorImplementation @Inject constructor( } } - //private var lastBcrId = -1L - //private var lastBcrTime = -1L - override fun processChangedBolusCalculatorResultsCompat(): Boolean { + override tailrec fun processChangedBolusCalculatorResultsCompat() { val lastDbIdWrapped = appRepository.getLastBolusCalculatorResultIdWrapped().blockingGet() val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L var startId = sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0) @@ -279,44 +263,46 @@ class DataSyncSelectorImplementation @Inject constructor( sp.putLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0) startId = 0 } - //if (startId == lastBcrId && dateUtil.now() - lastBcrTime < 5000) return false - //lastBcrId = startId - //lastBcrTime = dateUtil.now() queueCounter.bcrRemaining = lastDbId - startId appRepository.getNextSyncElementBolusCalculatorResult(startId).blockingGet()?.let { bolusCalculatorResult -> aapsLogger.info(LTag.NSCLIENT, "Loading BolusCalculatorResult data Start: $startId ID: ${bolusCalculatorResult.first.id} HistoryID: ${bolusCalculatorResult.second.id} ") when { - // only NsId changed, no need to upload - bolusCalculatorResult.first.onlyNsIdAdded(bolusCalculatorResult.second) -> { + // 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.first.id} HistoryID: ${bolusCalculatorResult.second.id} ") confirmLastBolusCalculatorResultsIdIfGreater(bolusCalculatorResult.second.id) - //lastBcrId = -1 processChangedBolusCalculatorResultsCompat() + 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.first.id} HistoryID: ${bolusCalculatorResult.second.id} ") - return false + confirmLastBolusCalculatorResultsIdIfGreater(bolusCalculatorResult.second.id) + processChangedBolusCalculatorResultsCompat() + return } // without nsId = create new - bolusCalculatorResult.first.interfaceIDs.nightscoutId == null -> - nsClientPlugin.nsClientService?.dbAdd( + bolusCalculatorResult.first.interfaceIDs.nightscoutId == null -> + activePlugin.activeNsClient?.nsClientService?.dbAdd( "treatments", bolusCalculatorResult.first.toJson(true, dateUtil, profileFunction), DataSyncSelector.PairBolusCalculatorResult(bolusCalculatorResult.first, bolusCalculatorResult.second.id), "$startId/$lastDbId" ) - // with nsId = update - bolusCalculatorResult.first.interfaceIDs.nightscoutId != null -> - nsClientPlugin.nsClientService?.dbUpdate( + // with nsId = update if it's modified record + bolusCalculatorResult.first.interfaceIDs.nightscoutId != null && bolusCalculatorResult.first.id != bolusCalculatorResult.second.id -> + activePlugin.activeNsClient?.nsClientService?.dbUpdate( "treatments", bolusCalculatorResult.first.interfaceIDs.nightscoutId, bolusCalculatorResult.first.toJson(false, dateUtil, profileFunction), DataSyncSelector.PairBolusCalculatorResult(bolusCalculatorResult.first, bolusCalculatorResult.second.id), "$startId/$lastDbId" ) } - return true + return } - return false } 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") + //aapsLogger.debug(LTag.NSCLIENT, "Setting TemporaryTarget data sync from $lastSynced") sp.putLong(R.string.key_ns_temporary_target_last_synced_id, lastSynced) } } @@ -329,9 +315,7 @@ class DataSyncSelectorImplementation @Inject constructor( } } - //private var lastTtId = -1L - //private var lastTtTime = -1L - override fun processChangedTempTargetsCompat(): Boolean { + override tailrec fun processChangedTempTargetsCompat() { val lastDbIdWrapped = appRepository.getLastTempTargetIdWrapped().blockingGet() val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L var startId = sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0) @@ -339,40 +323,35 @@ class DataSyncSelectorImplementation @Inject constructor( sp.putLong(R.string.key_ns_temporary_target_last_synced_id, 0) startId = 0 } - //if (startId == lastTtId && dateUtil.now() - lastTtTime < 5000) return false - //lastTtId = startId - //lastTtTime = dateUtil.now() queueCounter.ttsRemaining = lastDbId - startId appRepository.getNextSyncElementTemporaryTarget(startId).blockingGet()?.let { tt -> aapsLogger.info(LTag.NSCLIENT, "Loading TemporaryTarget data Start: $startId ID: ${tt.first.id} HistoryID: ${tt.second.id} ") when { - // record is not valid record and we are within first sync, no need to upload - tt.first.id != tt.second.id && tt.second.id <= sp.getLong(R.string.key_ns_temporary_target_new_data_id, 0) -> { + // 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.first.id} HistoryID: ${tt.second.id} ") confirmLastTempTargetsIdIfGreater(tt.second.id) - //lastTbrId = -1 processChangedTempTargetsCompat() - aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryTarget. Change within first sync ID: ${tt.first.id} HistoryID: ${tt.second.id} ") - return false + return } // only NsId changed, no need to upload - tt.first.onlyNsIdAdded(tt.second) -> { - confirmLastTempTargetsIdIfGreater(tt.second.id) - //lastTtId = -1 - processChangedTempTargetsCompat() + tt.first.onlyNsIdAdded(tt.second) -> { aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryTarget. Only NS id changed ID: ${tt.first.id} HistoryID: ${tt.second.id} ") - return false + confirmLastTempTargetsIdIfGreater(tt.second.id) + processChangedTempTargetsCompat() + return } // without nsId = create new - tt.first.interfaceIDs.nightscoutId == null -> - nsClientPlugin.nsClientService?.dbAdd( + tt.first.interfaceIDs.nightscoutId == null -> + activePlugin.activeNsClient?.nsClientService?.dbAdd( "treatments", tt.first.toJson(true, profileFunction.getUnits(), dateUtil), DataSyncSelector.PairTemporaryTarget(tt.first, tt.second.id), "$startId/$lastDbId" ) // existing with nsId = update - tt.first.interfaceIDs.nightscoutId != null -> - nsClientPlugin.nsClientService?.dbUpdate( + tt.first.interfaceIDs.nightscoutId != null -> + activePlugin.activeNsClient?.nsClientService?.dbUpdate( "treatments", tt.first.interfaceIDs.nightscoutId, tt.first.toJson(false, profileFunction.getUnits(), dateUtil), @@ -380,14 +359,13 @@ class DataSyncSelectorImplementation @Inject constructor( "$startId/$lastDbId" ) } - return true + return } - return false } 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") + //aapsLogger.debug(LTag.NSCLIENT, "Setting Food data sync from $lastSynced") sp.putLong(R.string.key_ns_food_last_synced_id, lastSynced) } } @@ -400,9 +378,7 @@ class DataSyncSelectorImplementation @Inject constructor( } } - //private var lastFoodId = -1L - //private var lastFoodTime = -1L - override fun processChangedFoodsCompat(): Boolean { + override tailrec fun processChangedFoodsCompat() { val lastDbIdWrapped = appRepository.getLastFoodIdWrapped().blockingGet() val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L var startId = sp.getLong(R.string.key_ns_food_last_synced_id, 0) @@ -410,27 +386,30 @@ class DataSyncSelectorImplementation @Inject constructor( sp.putLong(R.string.key_ns_food_last_synced_id, 0) startId = 0 } - //if (startId == lastFoodId && dateUtil.now() - lastFoodTime < 5000) return false - //lastFoodId = startId - //lastFoodTime = dateUtil.now() queueCounter.foodsRemaining = lastDbId - startId appRepository.getNextSyncElementFood(startId).blockingGet()?.let { food -> aapsLogger.info(LTag.NSCLIENT, "Loading Food data Start: $startId ID: ${food.first.id} HistoryID: ${food.second} ") when { - // only NsId changed, no need to upload - food.first.onlyNsIdAdded(food.second) -> { + // 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.first.id} HistoryID: ${food.second.id} ") confirmLastFoodIdIfGreater(food.second.id) - //lastFoodId = -1 processChangedFoodsCompat() + 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.first.id} HistoryID: ${food.second.id} ") - return false + confirmLastFoodIdIfGreater(food.second.id) + processChangedFoodsCompat() + return } // without nsId = create new - food.first.interfaceIDs.nightscoutId == null -> - nsClientPlugin.nsClientService?.dbAdd("food", food.first.toJson(true), DataSyncSelector.PairFood(food.first, food.second.id), "$startId/$lastDbId") + food.first.interfaceIDs.nightscoutId == null -> + activePlugin.activeNsClient?.nsClientService?.dbAdd("food", food.first.toJson(true), DataSyncSelector.PairFood(food.first, food.second.id), "$startId/$lastDbId") // with nsId = update - food.first.interfaceIDs.nightscoutId != null -> - nsClientPlugin.nsClientService?.dbUpdate( + food.first.interfaceIDs.nightscoutId != null -> + activePlugin.activeNsClient?.nsClientService?.dbUpdate( "food", food.first.interfaceIDs.nightscoutId, food.first.toJson(false), @@ -438,14 +417,13 @@ class DataSyncSelectorImplementation @Inject constructor( "$startId/$lastDbId" ) } - return true + return } - return false } 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") + //aapsLogger.debug(LTag.NSCLIENT, "Setting GlucoseValue data sync from $lastSynced") sp.putLong(R.string.key_ns_glucose_value_last_synced_id, lastSynced) } } @@ -458,8 +436,6 @@ class DataSyncSelectorImplementation @Inject constructor( } } - //private var lastGvId = -1L - //private var lastGvTime = -1L override tailrec fun processChangedGlucoseValuesCompat() { val lastDbIdWrapped = appRepository.getLastGlucoseValueIdWrapped().blockingGet() val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L @@ -468,35 +444,31 @@ class DataSyncSelectorImplementation @Inject constructor( sp.putLong(R.string.key_ns_glucose_value_last_synced_id, 0) startId = 0 } - //if (startId == lastGvId && dateUtil.now() - lastGvTime < 5000) return false - //lastGvId = startId - //lastGvTime = dateUtil.now() queueCounter.gvsRemaining = lastDbId - startId - var tailCall = false appRepository.getNextSyncElementGlucoseValue(startId).blockingGet()?.let { gv -> aapsLogger.info(LTag.NSCLIENT, "Loading GlucoseValue data ID: ${gv.first.id} HistoryID: ${gv.second.id} ") if (activePlugin.activeBgSource.shouldUploadToNs(gv.first)) { when { - // record is not valid record and we are within first sync, no need to upload - gv.first.id != gv.second.id && gv.second.id <= sp.getLong(R.string.key_ns_glucose_value_new_data_id, 0) -> { + // 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.first.id} HistoryID: ${gv.second.id} ") confirmLastGlucoseValueIdIfGreater(gv.second.id) - //lastGvId = -1 - aapsLogger.info(LTag.NSCLIENT, "Ignoring GlucoseValue. Change within first sync ID: ${gv.first.id} HistoryID: ${gv.second.id} ") - tailCall = true + processChangedGlucoseValuesCompat() + return } // only NsId changed, no need to upload - gv.first.onlyNsIdAdded(gv.second) -> { - confirmLastGlucoseValueIdIfGreater(gv.second.id) - //lastGvId = -1 + gv.first.onlyNsIdAdded(gv.second) -> { aapsLogger.info(LTag.NSCLIENT, "Ignoring GlucoseValue. Only NS id changed ID: ${gv.first.id} HistoryID: ${gv.second.id} ") - tailCall = true + confirmLastGlucoseValueIdIfGreater(gv.second.id) + processChangedGlucoseValuesCompat() + return } // without nsId = create new - gv.first.interfaceIDs.nightscoutId == null -> - nsClientPlugin.nsClientService?.dbAdd("entries", gv.first.toJson(true, dateUtil), DataSyncSelector.PairGlucoseValue(gv.first, gv.second.id), "$startId/$lastDbId") + gv.first.interfaceIDs.nightscoutId == null -> + activePlugin.activeNsClient?.nsClientService?.dbAdd("entries", gv.first.toJson(true, dateUtil), DataSyncSelector.PairGlucoseValue(gv.first, gv.second.id), "$startId/$lastDbId") // with nsId = update - else -> // gv.first.interfaceIDs.nightscoutId != null - nsClientPlugin.nsClientService?.dbUpdate( + else -> // gv.first.interfaceIDs.nightscoutId != null + activePlugin.activeNsClient?.nsClientService?.dbUpdate( "entries", gv.first.interfaceIDs.nightscoutId, gv.first.toJson(false, dateUtil), @@ -506,18 +478,15 @@ class DataSyncSelectorImplementation @Inject constructor( } } else { confirmLastGlucoseValueIdIfGreater(gv.second.id) - //lastGvId = -1 - tailCall = true + processChangedGlucoseValuesCompat() + return } } - if (tailCall) { - processChangedGlucoseValuesCompat() - } } 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") + //aapsLogger.debug(LTag.NSCLIENT, "Setting TherapyEvents data sync from $lastSynced") sp.putLong(R.string.key_ns_therapy_event_last_synced_id, lastSynced) } } @@ -530,9 +499,7 @@ class DataSyncSelectorImplementation @Inject constructor( } } - //private var lastTeId = -1L - //private var lastTeTime = -1L - override fun processChangedTherapyEventsCompat(): Boolean { + override tailrec fun processChangedTherapyEventsCompat() { val lastDbIdWrapped = appRepository.getLastTherapyEventIdWrapped().blockingGet() val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L var startId = sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0) @@ -540,27 +507,30 @@ class DataSyncSelectorImplementation @Inject constructor( sp.putLong(R.string.key_ns_therapy_event_last_synced_id, 0) startId = 0 } - //if (startId == lastTeId && dateUtil.now() - lastTeTime < 5000) return false - //lastTeId = startId - //lastTeTime = dateUtil.now() queueCounter.tesRemaining = lastDbId - startId appRepository.getNextSyncElementTherapyEvent(startId).blockingGet()?.let { te -> aapsLogger.info(LTag.NSCLIENT, "Loading TherapyEvents data Start: $startId ID: ${te.first.id} HistoryID: ${te.second} ") when { - // only NsId changed, no need to upload - te.first.onlyNsIdAdded(te.second) -> { + // 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.first.id} HistoryID: ${te.second.id} ") confirmLastTherapyEventIdIfGreater(te.second.id) - //lastTeId = -1 processChangedTherapyEventsCompat() - aapsLogger.info(LTag.NSCLIENT, "Ignoring TherapyEvents. Only NS id changed ID: ${te.first.id} HistoryID: ${te.second.id} ") - return false + 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.first.id} HistoryID: ${te.second.id} ") + confirmLastTherapyEventIdIfGreater(te.second.id) + processChangedTherapyEventsCompat() + return } // without nsId = create new - te.first.interfaceIDs.nightscoutId == null -> - nsClientPlugin.nsClientService?.dbAdd("treatments", te.first.toJson(true, dateUtil), DataSyncSelector.PairTherapyEvent(te.first, te.second.id), "$startId/$lastDbId") + te.first.interfaceIDs.nightscoutId == null -> + activePlugin.activeNsClient?.nsClientService?.dbAdd("treatments", te.first.toJson(true, dateUtil), DataSyncSelector.PairTherapyEvent(te.first, te.second.id), "$startId/$lastDbId") // nsId = update - te.first.interfaceIDs.nightscoutId != null -> - nsClientPlugin.nsClientService?.dbUpdate( + te.first.interfaceIDs.nightscoutId != null -> + activePlugin.activeNsClient?.nsClientService?.dbUpdate( "treatments", te.first.interfaceIDs.nightscoutId, te.first.toJson(false, dateUtil), @@ -568,14 +538,13 @@ class DataSyncSelectorImplementation @Inject constructor( "$startId/$lastDbId" ) } - return true + return } - return false } 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") + //aapsLogger.debug(LTag.NSCLIENT, "Setting DeviceStatus data sync from $lastSynced") sp.putLong(R.string.key_ns_device_status_last_synced_id, lastSynced) } } @@ -587,9 +556,7 @@ class DataSyncSelectorImplementation @Inject constructor( } } - //private var lastDsId = -1L - //private var lastDsTime = -1L - override fun processChangedDeviceStatusesCompat(): Boolean { + override fun processChangedDeviceStatusesCompat() { val lastDbIdWrapped = appRepository.getLastDeviceStatusIdWrapped().blockingGet() val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L var startId = sp.getLong(R.string.key_ns_device_status_last_synced_id, 0) @@ -597,27 +564,23 @@ class DataSyncSelectorImplementation @Inject constructor( sp.putLong(R.string.key_ns_device_status_last_synced_id, 0) startId = 0 } - //if (startId == lastDsId && dateUtil.now() - lastDsTime < 5000) return false - //lastDsId = startId - //lastDsTime = dateUtil.now() queueCounter.dssRemaining = lastDbId - startId appRepository.getNextSyncElementDeviceStatus(startId).blockingGet()?.let { deviceStatus -> aapsLogger.info(LTag.NSCLIENT, "Loading DeviceStatus data Start: $startId ID: ${deviceStatus.id}") when { // without nsId = create new deviceStatus.interfaceIDs.nightscoutId == null -> - nsClientPlugin.nsClientService?.dbAdd("devicestatus", deviceStatus.toJson(dateUtil), deviceStatus, "$startId/$lastDbId") + activePlugin.activeNsClient?.nsClientService?.dbAdd("devicestatus", deviceStatus.toJson(dateUtil), deviceStatus, "$startId/$lastDbId") // with nsId = ignore deviceStatus.interfaceIDs.nightscoutId != null -> Any() } - return true + return } - return false } 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") + //aapsLogger.debug(LTag.NSCLIENT, "Setting TemporaryBasal data sync from $lastSynced") sp.putLong(R.string.key_ns_temporary_basal_last_synced_id, lastSynced) } } @@ -630,9 +593,7 @@ class DataSyncSelectorImplementation @Inject constructor( } } - //private var lastTbrId = -1L - //private var lastTbrTime = -1L - override fun processChangedTemporaryBasalsCompat(): Boolean { + override tailrec fun processChangedTemporaryBasalsCompat() { val lastDbIdWrapped = appRepository.getLastTemporaryBasalIdWrapped().blockingGet() val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L var startId = sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0) @@ -640,42 +601,37 @@ class DataSyncSelectorImplementation @Inject constructor( sp.putLong(R.string.key_ns_temporary_basal_last_synced_id, 0) startId = 0 } - //if (startId == lastTbrId && dateUtil.now() - lastTbrTime < 5000) return false - //lastTbrId = startId - //lastTbrTime = dateUtil.now() queueCounter.tbrsRemaining = lastDbId - startId appRepository.getNextSyncElementTemporaryBasal(startId).blockingGet()?.let { tb -> aapsLogger.info(LTag.NSCLIENT, "Loading TemporaryBasal data Start: $startId ID: ${tb.first.id} HistoryID: ${tb.second} ") val profile = profileFunction.getProfile(tb.first.timestamp) if (profile != null) { when { - // record is not valid record and we are within first sync, no need to upload - tb.first.id != tb.second.id && tb.second.id <= sp.getLong(R.string.key_ns_temporary_basal_new_data_id, 0) -> { + // 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.first.id} HistoryID: ${tb.second.id} ") confirmLastTemporaryBasalIdIfGreater(tb.second.id) - //lastTbrId = -1 processChangedTemporaryBasalsCompat() - aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryBasal. Change within first sync ID: ${tb.first.id} HistoryID: ${tb.second.id} ") - return false + return } // only NsId changed, no need to upload - tb.first.onlyNsIdAdded(tb.second) -> { - confirmLastTemporaryBasalIdIfGreater(tb.second.id) - //lastTbrId = -1 - processChangedTemporaryBasalsCompat() + tb.first.onlyNsIdAdded(tb.second) -> { aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryBasal. Only NS id changed ID: ${tb.first.id} HistoryID: ${tb.second.id} ") - return false + confirmLastTemporaryBasalIdIfGreater(tb.second.id) + processChangedTemporaryBasalsCompat() + return } // without nsId = create new - tb.first.interfaceIDs.nightscoutId == null -> - nsClientPlugin.nsClientService?.dbAdd( + tb.first.interfaceIDs.nightscoutId == null -> + activePlugin.activeNsClient?.nsClientService?.dbAdd( "treatments", tb.first.toJson(true, profile, dateUtil), DataSyncSelector.PairTemporaryBasal(tb.first, tb.second.id), "$startId/$lastDbId" ) // with nsId = update - tb.first.interfaceIDs.nightscoutId != null -> - nsClientPlugin.nsClientService?.dbUpdate( + tb.first.interfaceIDs.nightscoutId != null -> + activePlugin.activeNsClient?.nsClientService?.dbUpdate( "treatments", tb.first.interfaceIDs.nightscoutId, tb.first.toJson(false, profile, dateUtil), @@ -683,19 +639,19 @@ class DataSyncSelectorImplementation @Inject constructor( "$startId/$lastDbId" ) } - return true + return } else { + aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryBasal. No profile: ${tb.first.id} HistoryID: ${tb.second.id} ") confirmLastTemporaryBasalIdIfGreater(tb.second.id) - //lastTbrId = -1 processChangedTemporaryBasalsCompat() + return } } - return false } 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") + //aapsLogger.debug(LTag.NSCLIENT, "Setting ExtendedBolus data sync from $lastSynced") sp.putLong(R.string.key_ns_extended_bolus_last_synced_id, lastSynced) } } @@ -708,9 +664,7 @@ class DataSyncSelectorImplementation @Inject constructor( } } - //private var lastEbId = -1L - //private var lastEbTime = -1L - override fun processChangedExtendedBolusesCompat(): Boolean { + override tailrec fun processChangedExtendedBolusesCompat() { val lastDbIdWrapped = appRepository.getLastExtendedBolusIdWrapped().blockingGet() val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L var startId = sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0) @@ -718,42 +672,37 @@ class DataSyncSelectorImplementation @Inject constructor( sp.putLong(R.string.key_ns_extended_bolus_last_synced_id, 0) startId = 0 } - //if (startId == lastEbId && dateUtil.now() - lastEbTime < 5000) return false - //lastEbId = startId - //lastEbTime = dateUtil.now() queueCounter.ebsRemaining = lastDbId - startId appRepository.getNextSyncElementExtendedBolus(startId).blockingGet()?.let { eb -> aapsLogger.info(LTag.NSCLIENT, "Loading ExtendedBolus data Start: $startId ID: ${eb.first.id} HistoryID: ${eb.second} ") val profile = profileFunction.getProfile(eb.first.timestamp) if (profile != null) { when { - // record is not valid record and we are within first sync, no need to upload - eb.first.id != eb.second.id && eb.second.id <= sp.getLong(R.string.key_ns_extended_bolus_new_data_id, 0) -> { + // 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.first.id} HistoryID: ${eb.second.id} ") confirmLastExtendedBolusIdIfGreater(eb.second.id) - //lastTbrId = -1 processChangedExtendedBolusesCompat() - aapsLogger.info(LTag.NSCLIENT, "Ignoring ExtendedBolus. Change within first sync ID: ${eb.first.id} HistoryID: ${eb.second.id} ") - return false + return } // only NsId changed, no need to upload - eb.first.onlyNsIdAdded(eb.second) -> { - confirmLastExtendedBolusIdIfGreater(eb.second.id) - //lastEbId = -1 - processChangedExtendedBolusesCompat() + eb.first.onlyNsIdAdded(eb.second) -> { aapsLogger.info(LTag.NSCLIENT, "Ignoring ExtendedBolus. Only NS id changed ID: ${eb.first.id} HistoryID: ${eb.second.id} ") - return false + confirmLastExtendedBolusIdIfGreater(eb.second.id) + processChangedExtendedBolusesCompat() + return } // without nsId = create new - eb.first.interfaceIDs.nightscoutId == null -> - nsClientPlugin.nsClientService?.dbAdd( + eb.first.interfaceIDs.nightscoutId == null -> + activePlugin.activeNsClient?.nsClientService?.dbAdd( "treatments", eb.first.toJson(true, profile, dateUtil), DataSyncSelector.PairExtendedBolus(eb.first, eb.second.id), "$startId/$lastDbId" ) // with nsId = update - eb.first.interfaceIDs.nightscoutId != null -> - nsClientPlugin.nsClientService?.dbUpdate( + eb.first.interfaceIDs.nightscoutId != null -> + activePlugin.activeNsClient?.nsClientService?.dbUpdate( "treatments", eb.first.interfaceIDs.nightscoutId, eb.first.toJson(false, profile, dateUtil), @@ -761,19 +710,19 @@ class DataSyncSelectorImplementation @Inject constructor( "$startId/$lastDbId" ) } - return true + return } else { + aapsLogger.info(LTag.NSCLIENT, "Ignoring ExtendedBolus. No profile: ${eb.first.id} HistoryID: ${eb.second.id} ") confirmLastExtendedBolusIdIfGreater(eb.second.id) - //lastEbId = -1 processChangedExtendedBolusesCompat() + return } } - return false } 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") + //aapsLogger.debug(LTag.NSCLIENT, "Setting ProfileSwitch data sync from $lastSynced") sp.putLong(R.string.key_ns_profile_switch_last_synced_id, lastSynced) } } @@ -785,9 +734,7 @@ class DataSyncSelectorImplementation @Inject constructor( } } - //private var lastPsId = -1L - //private var lastPsTime = -1L - override fun processChangedProfileSwitchesCompat(): Boolean { + override tailrec fun processChangedProfileSwitchesCompat() { val lastDbIdWrapped = appRepository.getLastProfileSwitchIdWrapped().blockingGet() val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L var startId = sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0) @@ -795,27 +742,30 @@ class DataSyncSelectorImplementation @Inject constructor( sp.putLong(R.string.key_ns_profile_switch_last_synced_id, 0) startId = 0 } - //if (startId == lastPsId && dateUtil.now() - lastPsTime < 5000) return false - //lastPsId = startId - //lastPsTime = dateUtil.now() queueCounter.pssRemaining = lastDbId - startId appRepository.getNextSyncElementProfileSwitch(startId).blockingGet()?.let { ps -> aapsLogger.info(LTag.NSCLIENT, "Loading ProfileSwitch data Start: $startId ID: ${ps.first.id} HistoryID: ${ps.second} ") when { - // only NsId changed, no need to upload - ps.first.onlyNsIdAdded(ps.second) -> { + // 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.first.id} HistoryID: ${ps.second.id} ") confirmLastProfileSwitchIdIfGreater(ps.second.id) - //lastPsId = -1 processChangedProfileSwitchesCompat() + 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.first.id} HistoryID: ${ps.second.id} ") - return false + confirmLastProfileSwitchIdIfGreater(ps.second.id) + processChangedProfileSwitchesCompat() + return } // without nsId = create new - ps.first.interfaceIDs.nightscoutId == null -> - nsClientPlugin.nsClientService?.dbAdd("treatments", ps.first.toJson(true, dateUtil), DataSyncSelector.PairProfileSwitch(ps.first, ps.second.id), "$startId/$lastDbId") + ps.first.interfaceIDs.nightscoutId == null -> + activePlugin.activeNsClient?.nsClientService?.dbAdd("treatments", ps.first.toJson(true, dateUtil), DataSyncSelector.PairProfileSwitch(ps.first, ps.second.id), "$startId/$lastDbId") // with nsId = update - ps.first.interfaceIDs.nightscoutId != null -> - nsClientPlugin.nsClientService?.dbUpdate( + ps.first.interfaceIDs.nightscoutId != null -> + activePlugin.activeNsClient?.nsClientService?.dbUpdate( "treatments", ps.first.interfaceIDs.nightscoutId, ps.first.toJson(false, dateUtil), @@ -823,14 +773,13 @@ class DataSyncSelectorImplementation @Inject constructor( "$startId/$lastDbId" ) } - return true + return } - return false } 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") + //aapsLogger.debug(LTag.NSCLIENT, "Setting EffectiveProfileSwitch data sync from $lastSynced") sp.putLong(R.string.key_ns_effective_profile_switch_last_synced_id, lastSynced) } } @@ -842,9 +791,7 @@ class DataSyncSelectorImplementation @Inject constructor( } } - //private var lastEpsId = -1L - //private var lastEpsTime = -1L - override fun processChangedEffectiveProfileSwitchesCompat(): Boolean { + override tailrec fun processChangedEffectiveProfileSwitchesCompat() { val lastDbIdWrapped = appRepository.getLastEffectiveProfileSwitchIdWrapped().blockingGet() val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L var startId = sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0) @@ -852,27 +799,35 @@ class DataSyncSelectorImplementation @Inject constructor( sp.putLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0) startId = 0 } - //if (startId == lastEpsId && dateUtil.now() - lastEpsTime < 5000) return false - //lastEpsId = startId - //lastEpsTime = dateUtil.now() queueCounter.epssRemaining = lastDbId - startId appRepository.getNextSyncElementEffectiveProfileSwitch(startId).blockingGet()?.let { ps -> aapsLogger.info(LTag.NSCLIENT, "Loading EffectiveProfileSwitch data Start: $startId ID: ${ps.first.id} HistoryID: ${ps.second} ") when { - // only NsId changed, no need to upload - ps.first.onlyNsIdAdded(ps.second) -> { + // 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.first.id} HistoryID: ${ps.second.id} ") confirmLastEffectiveProfileSwitchIdIfGreater(ps.second.id) - //lastEpsId = -1 processChangedEffectiveProfileSwitchesCompat() + 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.first.id} HistoryID: ${ps.second.id} ") - return false + confirmLastEffectiveProfileSwitchIdIfGreater(ps.second.id) + processChangedEffectiveProfileSwitchesCompat() + return } // without nsId = create new - ps.first.interfaceIDs.nightscoutId == null -> - nsClientPlugin.nsClientService?.dbAdd("treatments", ps.first.toJson(true, dateUtil), DataSyncSelector.PairEffectiveProfileSwitch(ps.first, ps.second.id), "$startId/$lastDbId") + ps.first.interfaceIDs.nightscoutId == null -> + activePlugin.activeNsClient?.nsClientService?.dbAdd( + "treatments", + ps.first.toJson(true, dateUtil), + DataSyncSelector.PairEffectiveProfileSwitch(ps.first, ps.second.id), + "$startId/$lastDbId" + ) // with nsId = update - ps.first.interfaceIDs.nightscoutId != null -> - nsClientPlugin.nsClientService?.dbUpdate( + ps.first.interfaceIDs.nightscoutId != null -> + activePlugin.activeNsClient?.nsClientService?.dbUpdate( "treatments", ps.first.interfaceIDs.nightscoutId, ps.first.toJson(false, dateUtil), @@ -880,14 +835,13 @@ class DataSyncSelectorImplementation @Inject constructor( "$startId/$lastDbId" ) } - return true + return } - return false } 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") + //aapsLogger.debug(LTag.NSCLIENT, "Setting OfflineEvent data sync from $lastSynced") sp.putLong(R.string.key_ns_offline_event_last_synced_id, lastSynced) } } @@ -900,9 +854,7 @@ class DataSyncSelectorImplementation @Inject constructor( } } - //private var lastOeId = -1L - //private var lastOeTime = -1L - override fun processChangedOfflineEventsCompat(): Boolean { + override tailrec fun processChangedOfflineEventsCompat() { val lastDbIdWrapped = appRepository.getLastOfflineEventIdWrapped().blockingGet() val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L var startId = sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0) @@ -910,27 +862,30 @@ class DataSyncSelectorImplementation @Inject constructor( sp.putLong(R.string.key_ns_offline_event_last_synced_id, 0) startId = 0 } - //if (startId == lastOeId && dateUtil.now() - lastOeTime < 5000) return false - //lastOeId = startId - //lastOeTime = dateUtil.now() queueCounter.oesRemaining = lastDbId - startId appRepository.getNextSyncElementOfflineEvent(startId).blockingGet()?.let { oe -> aapsLogger.info(LTag.NSCLIENT, "Loading OfflineEvent data Start: $startId ID: ${oe.first.id} HistoryID: ${oe.second} ") when { - // only NsId changed, no need to upload - oe.first.onlyNsIdAdded(oe.second) -> { + // 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.first.id} HistoryID: ${oe.second.id} ") confirmLastOfflineEventIdIfGreater(oe.second.id) - //lastOeId = -1 processChangedOfflineEventsCompat() + 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.first.id} HistoryID: ${oe.second.id} ") - return false + confirmLastOfflineEventIdIfGreater(oe.second.id) + processChangedOfflineEventsCompat() + return } // without nsId = create new - oe.first.interfaceIDs.nightscoutId == null -> - nsClientPlugin.nsClientService?.dbAdd("treatments", oe.first.toJson(true, dateUtil), DataSyncSelector.PairOfflineEvent(oe.first, oe.second.id), "$startId/$lastDbId") + oe.first.interfaceIDs.nightscoutId == null -> + activePlugin.activeNsClient?.nsClientService?.dbAdd("treatments", oe.first.toJson(true, dateUtil), DataSyncSelector.PairOfflineEvent(oe.first, oe.second.id), "$startId/$lastDbId") // existing with nsId = update - oe.first.interfaceIDs.nightscoutId != null -> - nsClientPlugin.nsClientService?.dbUpdate( + oe.first.interfaceIDs.nightscoutId != null -> + activePlugin.activeNsClient?.nsClientService?.dbUpdate( "treatments", oe.first.interfaceIDs.nightscoutId, oe.first.toJson(false, dateUtil), @@ -938,9 +893,8 @@ class DataSyncSelectorImplementation @Inject constructor( "$startId/$lastDbId" ) } - return true + return } - return false } override fun confirmLastProfileStore(lastSynced: Long) { @@ -954,7 +908,7 @@ class DataSyncSelectorImplementation @Inject constructor( if (lastChange > lastSync) { if (profilePlugin.profile?.allProfilesValid != true) return val profileJson = profilePlugin.profile?.data ?: return - nsClientPlugin.nsClientService?.dbAdd("profile", profileJson, DataSyncSelector.PairProfileStore(profileJson, dateUtil.now()), "") + activePlugin.activeNsClient?.nsClientService?.dbAdd("profile", profileJson, DataSyncSelector.PairProfileStore(profileJson, dateUtil.now()), "") } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddAckWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NSClientAddAckWorker.kt similarity index 89% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddAckWorker.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NSClientAddAckWorker.kt index 14a3786a2b..65ecac8683 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddAckWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NSClientAddAckWorker.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient +package info.nightscout.androidaps.plugins.sync.nsclient import android.content.Context import android.os.SystemClock @@ -36,8 +36,9 @@ import info.nightscout.androidaps.interfaces.DataSyncSelector.PairProfileSwitch import info.nightscout.androidaps.interfaces.DataSyncSelector.PairTemporaryBasal import info.nightscout.androidaps.interfaces.DataSyncSelector.PairTemporaryTarget import info.nightscout.androidaps.interfaces.DataSyncSelector.PairTherapyEvent -import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAddAck -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog +import info.nightscout.androidaps.interfaces.NsClient +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientNewLog +import info.nightscout.androidaps.plugins.sync.nsclient.acks.NSAddAck import info.nightscout.androidaps.receivers.DataWorkerStorage import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus @@ -68,7 +69,7 @@ class NSClientAddAckWorker( if (sp.getBoolean(R.string.key_ns_sync_slow, false)) SystemClock.sleep(1000) when (ack.originalObject) { - is PairTemporaryTarget -> { + is PairTemporaryTarget -> { val pair = ack.originalObject pair.value.interfaceIDs.nightscoutId = ack.id repository.runTransactionForResult(UpdateNsIdTemporaryTargetTransaction(pair.value)) @@ -82,12 +83,12 @@ class NSClientAddAckWorker( dataSyncSelector.confirmLastTempTargetsIdIfGreater(pair.updateRecordId) } .blockingGet() - rxBus.send(EventNSClientNewLog("DBADD", "Acked TemporaryTarget " + pair.value.interfaceIDs.nightscoutId)) + rxBus.send(EventNSClientNewLog("DBADD", "Acked TemporaryTarget " + pair.value.interfaceIDs.nightscoutId, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedTempTargetsCompat() } - is PairGlucoseValue -> { + is PairGlucoseValue -> { val pair = ack.originalObject pair.value.interfaceIDs.nightscoutId = ack.id repository.runTransactionForResult(UpdateNsIdGlucoseValueTransaction(pair.value)) @@ -101,12 +102,12 @@ class NSClientAddAckWorker( dataSyncSelector.confirmLastGlucoseValueIdIfGreater(pair.updateRecordId) } .blockingGet() - rxBus.send(EventNSClientNewLog("DBADD", "Acked GlucoseValue " + pair.value.interfaceIDs.nightscoutId)) + rxBus.send(EventNSClientNewLog("DBADD", "Acked GlucoseValue " + pair.value.interfaceIDs.nightscoutId, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedGlucoseValuesCompat() } - is PairFood -> { + is PairFood -> { val pair = ack.originalObject pair.value.interfaceIDs.nightscoutId = ack.id repository.runTransactionForResult(UpdateNsIdFoodTransaction(pair.value)) @@ -120,12 +121,12 @@ class NSClientAddAckWorker( dataSyncSelector.confirmLastFoodIdIfGreater(pair.updateRecordId) } .blockingGet() - rxBus.send(EventNSClientNewLog("DBADD", "Acked Food " + pair.value.interfaceIDs.nightscoutId)) + rxBus.send(EventNSClientNewLog("DBADD", "Acked Food " + pair.value.interfaceIDs.nightscoutId, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedFoodsCompat() } - is PairTherapyEvent -> { + is PairTherapyEvent -> { val pair = ack.originalObject pair.value.interfaceIDs.nightscoutId = ack.id repository.runTransactionForResult(UpdateNsIdTherapyEventTransaction(pair.value)) @@ -139,12 +140,12 @@ class NSClientAddAckWorker( dataSyncSelector.confirmLastTherapyEventIdIfGreater(pair.updateRecordId) } .blockingGet() - rxBus.send(EventNSClientNewLog("DBADD", "Acked TherapyEvent " + pair.value.interfaceIDs.nightscoutId)) + rxBus.send(EventNSClientNewLog("DBADD", "Acked TherapyEvent " + pair.value.interfaceIDs.nightscoutId, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedTherapyEventsCompat() } - is PairBolus -> { + is PairBolus -> { val pair = ack.originalObject pair.value.interfaceIDs.nightscoutId = ack.id repository.runTransactionForResult(UpdateNsIdBolusTransaction(pair.value)) @@ -158,12 +159,12 @@ class NSClientAddAckWorker( dataSyncSelector.confirmLastBolusIdIfGreater(pair.updateRecordId) } .blockingGet() - rxBus.send(EventNSClientNewLog("DBADD", "Acked Bolus " + pair.value.interfaceIDs.nightscoutId)) + rxBus.send(EventNSClientNewLog("DBADD", "Acked Bolus " + pair.value.interfaceIDs.nightscoutId, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedBolusesCompat() } - is PairCarbs -> { + is PairCarbs -> { val pair = ack.originalObject pair.value.interfaceIDs.nightscoutId = ack.id repository.runTransactionForResult(UpdateNsIdCarbsTransaction(pair.value)) @@ -177,12 +178,12 @@ class NSClientAddAckWorker( dataSyncSelector.confirmLastCarbsIdIfGreater(pair.updateRecordId) } .blockingGet() - rxBus.send(EventNSClientNewLog("DBADD", "Acked Carbs " + pair.value.interfaceIDs.nightscoutId)) + rxBus.send(EventNSClientNewLog("DBADD", "Acked Carbs " + pair.value.interfaceIDs.nightscoutId, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedCarbsCompat() } - is PairBolusCalculatorResult -> { + is PairBolusCalculatorResult -> { val pair = ack.originalObject pair.value.interfaceIDs.nightscoutId = ack.id repository.runTransactionForResult(UpdateNsIdBolusCalculatorResultTransaction(pair.value)) @@ -196,12 +197,12 @@ class NSClientAddAckWorker( dataSyncSelector.confirmLastBolusCalculatorResultsIdIfGreater(pair.updateRecordId) } .blockingGet() - rxBus.send(EventNSClientNewLog("DBADD", "Acked BolusCalculatorResult " + pair.value.interfaceIDs.nightscoutId)) + rxBus.send(EventNSClientNewLog("DBADD", "Acked BolusCalculatorResult " + pair.value.interfaceIDs.nightscoutId, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedBolusCalculatorResultsCompat() } - is PairTemporaryBasal -> { + is PairTemporaryBasal -> { val pair = ack.originalObject pair.value.interfaceIDs.nightscoutId = ack.id repository.runTransactionForResult(UpdateNsIdTemporaryBasalTransaction(pair.value)) @@ -215,12 +216,12 @@ class NSClientAddAckWorker( dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(pair.updateRecordId) } .blockingGet() - rxBus.send(EventNSClientNewLog("DBADD", "Acked TemporaryBasal " + pair.value.interfaceIDs.nightscoutId)) + rxBus.send(EventNSClientNewLog("DBADD", "Acked TemporaryBasal " + pair.value.interfaceIDs.nightscoutId, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedTemporaryBasalsCompat() } - is PairExtendedBolus -> { + is PairExtendedBolus -> { val pair = ack.originalObject pair.value.interfaceIDs.nightscoutId = ack.id repository.runTransactionForResult(UpdateNsIdExtendedBolusTransaction(pair.value)) @@ -234,12 +235,12 @@ class NSClientAddAckWorker( dataSyncSelector.confirmLastExtendedBolusIdIfGreater(pair.updateRecordId) } .blockingGet() - rxBus.send(EventNSClientNewLog("DBADD", "Acked ExtendedBolus " + pair.value.interfaceIDs.nightscoutId)) + rxBus.send(EventNSClientNewLog("DBADD", "Acked ExtendedBolus " + pair.value.interfaceIDs.nightscoutId, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedExtendedBolusesCompat() } - is PairProfileSwitch -> { + is PairProfileSwitch -> { val pair = ack.originalObject pair.value.interfaceIDs.nightscoutId = ack.id repository.runTransactionForResult(UpdateNsIdProfileSwitchTransaction(pair.value)) @@ -253,12 +254,12 @@ class NSClientAddAckWorker( dataSyncSelector.confirmLastProfileSwitchIdIfGreater(pair.updateRecordId) } .blockingGet() - rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileSwitch " + pair.value.interfaceIDs.nightscoutId)) + rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileSwitch " + pair.value.interfaceIDs.nightscoutId, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedProfileSwitchesCompat() } - is PairEffectiveProfileSwitch -> { + is PairEffectiveProfileSwitch -> { val pair = ack.originalObject pair.value.interfaceIDs.nightscoutId = ack.id repository.runTransactionForResult(UpdateNsIdEffectiveProfileSwitchTransaction(pair.value)) @@ -272,12 +273,12 @@ class NSClientAddAckWorker( dataSyncSelector.confirmLastEffectiveProfileSwitchIdIfGreater(pair.updateRecordId) } .blockingGet() - rxBus.send(EventNSClientNewLog("DBADD", "Acked EffectiveProfileSwitch " + pair.value.interfaceIDs.nightscoutId)) + rxBus.send(EventNSClientNewLog("DBADD", "Acked EffectiveProfileSwitch " + pair.value.interfaceIDs.nightscoutId, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedEffectiveProfileSwitchesCompat() } - is DeviceStatus -> { + is DeviceStatus -> { val deviceStatus = ack.originalObject deviceStatus.interfaceIDs.nightscoutId = ack.id repository.runTransactionForResult(UpdateNsIdDeviceStatusTransaction(deviceStatus)) @@ -291,17 +292,17 @@ class NSClientAddAckWorker( dataSyncSelector.confirmLastDeviceStatusIdIfGreater(deviceStatus.id) } .blockingGet() - rxBus.send(EventNSClientNewLog("DBADD", "Acked DeviceStatus " + deviceStatus.interfaceIDs.nightscoutId)) + rxBus.send(EventNSClientNewLog("DBADD", "Acked DeviceStatus " + deviceStatus.interfaceIDs.nightscoutId, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedDeviceStatusesCompat() } - is PairProfileStore -> { + is PairProfileStore -> { dataSyncSelector.confirmLastProfileStore(ack.originalObject.timestampSync) - rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileStore " + ack.id)) + rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileStore " + ack.id, NsClient.Version.V1)) } - is PairOfflineEvent -> { + is PairOfflineEvent -> { val pair = ack.originalObject pair.value.interfaceIDs.nightscoutId = ack.id repository.runTransactionForResult(UpdateNsIdOfflineEventTransaction(pair.value)) @@ -315,7 +316,7 @@ class NSClientAddAckWorker( dataSyncSelector.confirmLastOfflineEventIdIfGreater(pair.updateRecordId) } .blockingGet() - rxBus.send(EventNSClientNewLog("DBADD", "Acked OfflineEvent " + pair.value.interfaceIDs.nightscoutId)) + rxBus.send(EventNSClientNewLog("DBADD", "Acked OfflineEvent " + pair.value.interfaceIDs.nightscoutId, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedOfflineEventsCompat() } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NSClientAddUpdateWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NSClientAddUpdateWorker.kt new file mode 100644 index 0000000000..be311e750f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NSClientAddUpdateWorker.kt @@ -0,0 +1,180 @@ +package info.nightscout.androidaps.plugins.sync.nsclient + +import android.content.Context +import androidx.work.Worker +import androidx.work.WorkerParameters +import androidx.work.workDataOf +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.R +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.extensions.bolusCalculatorResultFromJson +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.XDripBroadcast +import info.nightscout.androidaps.logging.UserEntryLogger +import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin +import info.nightscout.androidaps.plugins.sync.nsShared.StoreDataForDb +import info.nightscout.androidaps.receivers.DataWorkerStorage +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.interfaces.BuildHelper +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.utils.JsonHelper +import info.nightscout.plugins.sync.nsclient.extensions.bolusFromJson +import info.nightscout.plugins.sync.nsclient.extensions.carbsFromJson +import info.nightscout.plugins.sync.nsclient.extensions.effectiveProfileSwitchFromJson +import info.nightscout.plugins.sync.nsclient.extensions.extendedBolusFromJson +import info.nightscout.plugins.sync.nsclient.extensions.isEffectiveProfileSwitch +import info.nightscout.plugins.sync.nsclient.extensions.offlineEventFromJson +import info.nightscout.plugins.sync.nsclient.extensions.profileSwitchFromJson +import info.nightscout.plugins.sync.nsclient.extensions.temporaryBasalFromJson +import info.nightscout.plugins.sync.nsclient.extensions.temporaryTargetFromJson +import info.nightscout.plugins.sync.nsclient.extensions.therapyEventFromJson +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 javax.inject.Inject + +class NSClientAddUpdateWorker( + context: Context, + params: WorkerParameters +) : Worker(context, params) { + + @Inject lateinit var dataWorkerStorage: DataWorkerStorage + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var buildHelper: BuildHelper + @Inject lateinit var sp: SP + @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var config: Config + @Inject lateinit var repository: AppRepository + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var rxBus: RxBus + @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var virtualPumpPlugin: VirtualPumpPlugin + @Inject lateinit var xDripBroadcast: XDripBroadcast + @Inject lateinit var storeDataForDb: StoreDataForDb + + override fun doWork(): Result { + val treatments = dataWorkerStorage.pickupJSONArray(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) + ?: return Result.failure(workDataOf("Error" to "missing input data")) + + val ret = Result.success() + var latestDateInReceivedData = 0L + + for (i in 0 until treatments.length()) { + var json = treatments.getJSONObject(i) + aapsLogger.debug(LTag.DATABASE, "Received NS treatment: $json") + + val insulin = JsonHelper.safeGetDouble(json, "insulin") + val carbs = JsonHelper.safeGetDouble(json, "carbs") + var eventType = JsonHelper.safeGetString(json, "eventType") + if (eventType == null) { + aapsLogger.debug(LTag.NSCLIENT, "Wrong treatment. Ignoring : $json") + continue + } + + //Find latest date in treatment + val mills = JsonHelper.safeGetLong(json, "mills") + if (mills != 0L && mills < dateUtil.now()) + if (mills > latestDateInReceivedData) latestDateInReceivedData = mills + + if (insulin > 0) { + if (sp.getBoolean(R.string.key_ns_receive_insulin, false) || config.NSCLIENT) { + bolusFromJson(json)?.let { bolus -> + storeDataForDb.boluses.add(bolus) + } ?: aapsLogger.error("Error parsing bolus json $json") + } + } + if (carbs > 0) { + if (sp.getBoolean(R.string.key_ns_receive_carbs, false) || config.NSCLIENT) { + carbsFromJson(json)?.let { carb -> + storeDataForDb.carbs.add(carb) + } ?: aapsLogger.error("Error parsing bolus json $json") + } + } + // Convert back emulated TBR -> EB + if (eventType == TherapyEvent.Type.TEMPORARY_BASAL.text && json.has("extendedEmulated")) { + val ebJson = json.getJSONObject("extendedEmulated") + ebJson.put("_id", json.getString("_id")) + ebJson.put("isValid", json.getBoolean("isValid")) + ebJson.put("mills", mills) + json = ebJson + eventType = JsonHelper.safeGetString(json, "eventType") + virtualPumpPlugin.fakeDataDetected = true + } + when { + insulin > 0 || carbs > 0 -> Any() + eventType == TherapyEvent.Type.TEMPORARY_TARGET.text -> + if (sp.getBoolean(R.string.key_ns_receive_temp_target, false) || config.NSCLIENT) { + temporaryTargetFromJson(json)?.let { temporaryTarget -> + storeDataForDb.temporaryTargets.add(temporaryTarget) + } ?: aapsLogger.error("Error parsing TT json $json") + } + + eventType == TherapyEvent.Type.NOTE.text && json.isEffectiveProfileSwitch() -> // replace this by new Type when available in NS + if (sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || config.NSCLIENT) { + effectiveProfileSwitchFromJson(json, dateUtil)?.let { effectiveProfileSwitch -> + storeDataForDb.effectiveProfileSwitches.add(effectiveProfileSwitch) + } ?: aapsLogger.error("Error parsing EffectiveProfileSwitch json $json") + } + + eventType == TherapyEvent.Type.BOLUS_WIZARD.text -> + bolusCalculatorResultFromJson(json)?.let { bolusCalculatorResult -> + storeDataForDb.bolusCalculatorResults.add(bolusCalculatorResult) + } ?: aapsLogger.error("Error parsing BolusCalculatorResult json $json") + + eventType == TherapyEvent.Type.CANNULA_CHANGE.text || + eventType == TherapyEvent.Type.INSULIN_CHANGE.text || + eventType == TherapyEvent.Type.SENSOR_CHANGE.text || + eventType == TherapyEvent.Type.FINGER_STICK_BG_VALUE.text || + eventType == TherapyEvent.Type.NONE.text || + eventType == TherapyEvent.Type.ANNOUNCEMENT.text || + eventType == TherapyEvent.Type.QUESTION.text || + eventType == TherapyEvent.Type.EXERCISE.text || + eventType == TherapyEvent.Type.NOTE.text || + eventType == TherapyEvent.Type.PUMP_BATTERY_CHANGE.text -> + if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) { + therapyEventFromJson(json)?.let { therapyEvent -> + storeDataForDb.therapyEvents.add(therapyEvent) + } ?: aapsLogger.error("Error parsing TherapyEvent json $json") + } + + eventType == TherapyEvent.Type.COMBO_BOLUS.text -> + if (buildHelper.isEngineeringMode() && sp.getBoolean(R.string.key_ns_receive_tbr_eb, false) || config.NSCLIENT) { + extendedBolusFromJson(json)?.let { extendedBolus -> + storeDataForDb.extendedBoluses.add(extendedBolus) + } ?: aapsLogger.error("Error parsing ExtendedBolus json $json") + } + + eventType == TherapyEvent.Type.TEMPORARY_BASAL.text -> + if (buildHelper.isEngineeringMode() && sp.getBoolean(R.string.key_ns_receive_tbr_eb, false) || config.NSCLIENT) { + temporaryBasalFromJson(json)?.let { temporaryBasal -> + storeDataForDb.temporaryBasals.add(temporaryBasal) + } ?: aapsLogger.error("Error parsing TemporaryBasal json $json") + } + + eventType == TherapyEvent.Type.PROFILE_SWITCH.text -> + if (sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || config.NSCLIENT) { + profileSwitchFromJson(json, dateUtil, activePlugin)?.let { profileSwitch -> + storeDataForDb.profileSwitches.add(profileSwitch) + } ?: aapsLogger.error("Error parsing ProfileSwitch json $json") + } + + eventType == TherapyEvent.Type.APS_OFFLINE.text -> + if (sp.getBoolean(R.string.key_ns_receive_offline_event, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) { + offlineEventFromJson(json)?.let { offlineEvent -> + storeDataForDb.offlineEvents.add(offlineEvent) + } ?: aapsLogger.error("Error parsing OfflineEvent json $json") + } + } + } + storeDataForDb.storeTreatmentsToDb() + activePlugin.activeNsClient?.updateLatestTreatmentReceivedIfNewer(latestDateInReceivedData) + xDripBroadcast.sendTreatments(treatments) + return ret + } + + init { + (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientMbgWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NSClientMbgWorker.kt similarity index 54% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientMbgWorker.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NSClientMbgWorker.kt index d28234a4dc..bf7e05fa60 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientMbgWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NSClientMbgWorker.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient +package info.nightscout.androidaps.plugins.sync.nsclient import android.content.Context import androidx.work.Worker @@ -6,15 +6,11 @@ import androidx.work.WorkerParameters import androidx.work.workDataOf import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R -import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.database.transactions.SyncNsTherapyEventTransaction -import info.nightscout.androidaps.extensions.therapyEventFromNsMbg -import info.nightscout.interfaces.BuildHelper -import info.nightscout.interfaces.Config -import info.nightscout.androidaps.plugins.general.nsclient.data.NSMbg +import info.nightscout.androidaps.plugins.sync.nsShared.StoreDataForDb +import info.nightscout.androidaps.plugins.sync.nsclient.data.NSMbg import info.nightscout.androidaps.receivers.DataWorkerStorage -import info.nightscout.rx.logging.AAPSLogger -import info.nightscout.rx.logging.LTag +import info.nightscout.androidaps.utils.extensions.therapyEventFromNsMbg +import info.nightscout.interfaces.Config import info.nightscout.shared.sharedPreferences.SP import javax.inject.Inject @@ -23,15 +19,13 @@ class NSClientMbgWorker( params: WorkerParameters ) : Worker(context, params) { - @Inject lateinit var repository: AppRepository @Inject lateinit var dataWorkerStorage: DataWorkerStorage - @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var sp: SP - @Inject lateinit var buildHelper: BuildHelper @Inject lateinit var config: Config + @Inject lateinit var storeDataForDb: StoreDataForDb override fun doWork(): Result { - var ret = Result.success() + val ret = Result.success() val acceptNSData = sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT if (!acceptNSData) return Result.success(workDataOf("Result" to "Sync not enabled")) @@ -41,16 +35,9 @@ class NSClientMbgWorker( for (i in 0 until mbgArray.length()) { val nsMbg = NSMbg(mbgArray.getJSONObject(i)) if (!nsMbg.isValid()) continue - repository.runTransactionForResult(SyncNsTherapyEventTransaction(therapyEventFromNsMbg(nsMbg))) - .doOnError { - aapsLogger.error("Error while saving therapy event", it) - ret = Result.failure(workDataOf("Error" to it.toString())) - } - .blockingGet() - .also { - aapsLogger.debug(LTag.DATABASE, "Saved therapy event $it") - } + storeDataForDb.therapyEvents.add(therapyEventFromNsMbg(nsMbg)) } + // storeDataForDb.storeTreatmentsToDb() don't do this. It will be stored along with other treatments return ret } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NSClientPlugin.kt similarity index 71% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NSClientPlugin.kt index fbdf390f68..61a221f41f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NSClientPlugin.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient +package info.nightscout.androidaps.plugins.sync.nsclient import android.content.ComponentName import android.content.Context @@ -7,29 +7,34 @@ import android.content.ServiceConnection import android.os.Handler import android.os.HandlerThread import android.os.IBinder +import android.text.Spanned import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceScreen import androidx.preference.SwitchPreference import dagger.android.HasAndroidInjector -import info.nightscout.interfaces.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.events.EventPreferenceChange +import info.nightscout.androidaps.interfaces.DataSyncSelector +import info.nightscout.androidaps.interfaces.NsClient +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.androidaps.interfaces.Sync +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientUpdateGUI +import info.nightscout.androidaps.plugins.sync.nsShared.NSClientFragment +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientNewLog +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientResend +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientStatus +import info.nightscout.androidaps.plugins.sync.nsclient.data.AlarmAck +import info.nightscout.androidaps.plugins.sync.nsclient.data.NSAlarm +import info.nightscout.androidaps.plugins.sync.nsclient.services.NSClientService +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.interfaces.BuildHelper import info.nightscout.interfaces.Config -import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.interfaces.Constants import info.nightscout.interfaces.PluginDescription import info.nightscout.interfaces.PluginType -import info.nightscout.androidaps.interfaces.ResourceHelper -import info.nightscout.androidaps.plugins.general.nsclient.data.AlarmAck -import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientResend -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI -import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService -import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.interfaces.utils.HtmlHelper.fromHtml -import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventAppExit @@ -55,17 +60,16 @@ class NSClientPlugin @Inject constructor( private val sp: SP, private val nsClientReceiverDelegate: NsClientReceiverDelegate, private val config: Config, - private val buildHelper: BuildHelper -) : PluginBase( + private val buildHelper: BuildHelper, + private val dataSyncSelector: DataSyncSelector +) : NsClient, Sync, PluginBase( PluginDescription() - .mainType(PluginType.GENERAL) + .mainType(PluginType.SYNC) .fragmentClass(NSClientFragment::class.java.name) .pluginIcon(R.drawable.ic_nightscout_syncs) .pluginName(R.string.nsclientinternal) .shortName(R.string.nsclientinternal_shortname) .preferencesId(R.xml.pref_nsclientinternal) - .alwaysEnabled(config.NSCLIENT) - .visibleByDefault(config.NSCLIENT) .description(R.string.description_ns_client), aapsLogger, rh, injector ) { @@ -73,19 +77,14 @@ class NSClientPlugin @Inject constructor( private val disposable = CompositeDisposable() private val handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) private val listLog: MutableList = ArrayList() - var textLog = fromHtml("") - var paused = false - var autoscroll = false - var status = "" - var nsClientService: NSClientService? = null + override var status = "" + override var nsClientService: NSClientService? = null val isAllowed: Boolean get() = nsClientReceiverDelegate.allowed val blockingReason: String get() = nsClientReceiverDelegate.blockingReason override fun onStart() { - paused = sp.getBoolean(R.string.key_nsclientinternal_paused, false) - autoscroll = sp.getBoolean(R.string.key_nsclientinternal_autoscroll, true) context.bindService(Intent(context, NSClientService::class.java), mConnection, Context.BIND_AUTO_CREATE) super.onStart() nsClientReceiverDelegate.grabReceiversState() @@ -93,8 +92,10 @@ class NSClientPlugin @Inject constructor( .toObservable(EventNSClientStatus::class.java) .observeOn(aapsSchedulers.io) .subscribe({ event: EventNSClientStatus -> - status = event.getStatus(rh) - rxBus.send(EventNSClientUpdateGUI()) + if (event.version == NsClient.Version.V1) { + status = event.getStatus(rh) + rxBus.send(EventNSClientUpdateGUI()) + } }, fabricPrivacy::logException) disposable += rxBus .toObservable(EventNetworkChange::class.java) @@ -112,6 +113,7 @@ class NSClientPlugin @Inject constructor( .toObservable(EventNSClientNewLog::class.java) .observeOn(aapsSchedulers.io) .subscribe({ event: EventNSClientNewLog -> + if (event.version != NsClient.Version.V1) return@subscribe addToLog(event) aapsLogger.debug(LTag.NSCLIENT, event.action + " " + event.logText) }, fabricPrivacy::logException) @@ -138,17 +140,13 @@ class NSClientPlugin @Inject constructor( preferenceFragment.findPreference(rh.gs(R.string.key_ns_create_announcements_from_errors))?.isVisible = false preferenceFragment.findPreference(rh.gs(R.string.key_ns_create_announcements_from_carbs_req))?.isVisible = false -// preferenceFragment.findPreference(rh.gs(R.string.key_ns_sync_use_absolute))?.isVisible = false - } else { - // APS or pumpControl mode -// preferenceFragment.findPreference(rh.gs(R.string.key_ns_receive_profile_switch))?.isVisible = buildHelper.isEngineeringMode() -// preferenceFragment.findPreference(rh.gs(R.string.key_ns_receive_insulin))?.isVisible = buildHelper.isEngineeringMode() -// preferenceFragment.findPreference(rh.gs(R.string.key_ns_receive_carbs))?.isVisible = buildHelper.isEngineeringMode() -// preferenceFragment.findPreference(rh.gs(R.string.key_ns_receive_temp_target))?.isVisible = buildHelper.isEngineeringMode() } preferenceFragment.findPreference(rh.gs(R.string.key_ns_receive_tbr_eb))?.isVisible = buildHelper.isEngineeringMode() } + override val hasWritePermission: Boolean get() = nsClientService?.hasWriteAuth ?: false + override val connected: Boolean get() = nsClientService?.isConnected ?: false + private val mConnection: ServiceConnection = object : ServiceConnection { override fun onServiceDisconnected(name: ComponentName) { aapsLogger.debug(LTag.NSCLIENT, "Service is disconnected") @@ -162,55 +160,53 @@ class NSClientPlugin @Inject constructor( } } - @Synchronized fun clearLog() { + override fun clearLog() { handler.post { synchronized(listLog) { listLog.clear() } rxBus.send(EventNSClientUpdateGUI()) } } - @Synchronized private fun addToLog(ev: EventNSClientNewLog) { - handler.post { - synchronized(listLog) { - listLog.add(ev) - // remove the first line if log is too large - if (listLog.size >= Constants.MAX_LOG_LINES) { - listLog.removeAt(0) - } + private fun addToLog(ev: EventNSClientNewLog) { + synchronized(listLog) { + listLog.add(ev) + // remove the first line if log is too large + if (listLog.size >= Constants.MAX_LOG_LINES) { + listLog.removeAt(0) } - rxBus.send(EventNSClientUpdateGUI()) } + rxBus.send(EventNSClientUpdateGUI()) } - @Synchronized fun updateLog() { + override fun textLog(): Spanned { try { val newTextLog = StringBuilder() synchronized(listLog) { - for (log in listLog) { - newTextLog.append(log.toPreparedHtml()) - } + for (log in listLog) newTextLog.append(log.toPreparedHtml()) } - textLog = fromHtml(newTextLog.toString()) + return fromHtml(newTextLog.toString()) } catch (e: OutOfMemoryError) { ToastUtils.showToastInUiThread(context, rxBus, "Out of memory!\nStop using this phone !!!", R.raw.error) } + return fromHtml("") } - fun resend(reason: String) { + override fun resend(reason: String) { nsClientService?.resend(reason) } - fun pause(newState: Boolean) { + override fun pause(newState: Boolean) { sp.putBoolean(R.string.key_nsclientinternal_paused, newState) - paused = newState rxBus.send(EventPreferenceChange(rh, R.string.key_nsclientinternal_paused)) } - fun url(): String = nsClientService?.nsURL ?: "" - fun hasWritePermission(): Boolean = nsClientService?.hasWriteAuth ?: false + override val version: NsClient.Version + get() = NsClient.Version.V1 + + override val address: String get() = nsClientService?.nsURL ?: "" fun handleClearAlarm(originalAlarm: NSAlarm, silenceTimeInMilliseconds: Long) { - if (!isEnabled(PluginType.GENERAL)) return + if (!isEnabled()) return if (!sp.getBoolean(R.string.key_ns_upload, true)) { aapsLogger.debug(LTag.NSCLIENT, "Upload disabled. Message dropped") return @@ -223,7 +219,15 @@ class NSClientPlugin @Inject constructor( }) } - fun updateLatestDateReceivedIfNewer(latestReceived: Long) { + override fun updateLatestBgReceivedIfNewer(latestReceived: Long) { nsClientService?.let { if (latestReceived > it.latestDateInReceivedData) it.latestDateInReceivedData = latestReceived } } + + override fun updateLatestTreatmentReceivedIfNewer(latestReceived: Long) { + nsClientService?.let { if (latestReceived > it.latestDateInReceivedData) it.latestDateInReceivedData = latestReceived } + } + + override fun resetToFullSync() { + dataSyncSelector.resetToNextFullSync() + } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientUpdateRemoveAckWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NSClientUpdateRemoveAckWorker.kt similarity index 89% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientUpdateRemoveAckWorker.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NSClientUpdateRemoveAckWorker.kt index fa97e4a0e4..df4f77f049 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientUpdateRemoveAckWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NSClientUpdateRemoveAckWorker.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient +package info.nightscout.androidaps.plugins.sync.nsclient import android.content.Context import androidx.work.Worker @@ -19,8 +19,9 @@ import info.nightscout.androidaps.interfaces.DataSyncSelector.PairProfileSwitch import info.nightscout.androidaps.interfaces.DataSyncSelector.PairTemporaryBasal import info.nightscout.androidaps.interfaces.DataSyncSelector.PairTemporaryTarget import info.nightscout.androidaps.interfaces.DataSyncSelector.PairTherapyEvent -import info.nightscout.androidaps.plugins.general.nsclient.acks.NSUpdateAck -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog +import info.nightscout.androidaps.interfaces.NsClient +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientNewLog +import info.nightscout.androidaps.plugins.sync.nsclient.acks.NSUpdateAck import info.nightscout.androidaps.receivers.DataWorkerStorage import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus @@ -50,7 +51,7 @@ class NSClientUpdateRemoveAckWorker( is PairTemporaryTarget -> { val pair = ack.originalObject dataSyncSelector.confirmLastTempTargetsIdIfGreater(pair.updateRecordId) - rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TemporaryTarget" + ack._id)) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TemporaryTarget" + ack._id, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedTempTargetsCompat() ret = Result.success(workDataOf("ProcessedData" to pair.toString())) @@ -59,7 +60,7 @@ class NSClientUpdateRemoveAckWorker( is PairGlucoseValue -> { val pair = ack.originalObject dataSyncSelector.confirmLastGlucoseValueIdIfGreater(pair.updateRecordId) - rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked GlucoseValue " + ack._id)) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked GlucoseValue " + ack._id, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedGlucoseValuesCompat() ret = Result.success(workDataOf("ProcessedData" to pair.toString())) @@ -68,7 +69,7 @@ class NSClientUpdateRemoveAckWorker( is PairFood -> { val pair = ack.originalObject dataSyncSelector.confirmLastFoodIdIfGreater(pair.updateRecordId) - rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Food " + ack._id)) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Food " + ack._id, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedFoodsCompat() ret = Result.success(workDataOf("ProcessedData" to pair.toString())) @@ -77,7 +78,7 @@ class NSClientUpdateRemoveAckWorker( is PairTherapyEvent -> { val pair = ack.originalObject dataSyncSelector.confirmLastTherapyEventIdIfGreater(pair.updateRecordId) - rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TherapyEvent " + ack._id)) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TherapyEvent " + ack._id, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedTherapyEventsCompat() ret = Result.success(workDataOf("ProcessedData" to pair.toString())) @@ -86,7 +87,7 @@ class NSClientUpdateRemoveAckWorker( is PairBolus -> { val pair = ack.originalObject dataSyncSelector.confirmLastBolusIdIfGreater(pair.updateRecordId) - rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Bolus " + ack._id)) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Bolus " + ack._id, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedBolusesCompat() ret = Result.success(workDataOf("ProcessedData" to pair.toString())) @@ -95,7 +96,7 @@ class NSClientUpdateRemoveAckWorker( is PairCarbs -> { val pair = ack.originalObject dataSyncSelector.confirmLastCarbsIdIfGreater(pair.updateRecordId) - rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Carbs " + ack._id)) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Carbs " + ack._id, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedCarbsCompat() ret = Result.success(workDataOf("ProcessedData" to pair.toString())) @@ -104,7 +105,7 @@ class NSClientUpdateRemoveAckWorker( is PairBolusCalculatorResult -> { val pair = ack.originalObject dataSyncSelector.confirmLastBolusCalculatorResultsIdIfGreater(pair.updateRecordId) - rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked BolusCalculatorResult " + ack._id)) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked BolusCalculatorResult " + ack._id, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedBolusCalculatorResultsCompat() ret = Result.success(workDataOf("ProcessedData" to pair.toString())) @@ -113,7 +114,7 @@ class NSClientUpdateRemoveAckWorker( is PairTemporaryBasal -> { val pair = ack.originalObject dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(pair.updateRecordId) - rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TemporaryBasal " + ack._id)) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TemporaryBasal " + ack._id, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedTemporaryBasalsCompat() ret = Result.success(workDataOf("ProcessedData" to pair.toString())) @@ -122,7 +123,7 @@ class NSClientUpdateRemoveAckWorker( is PairExtendedBolus -> { val pair = ack.originalObject dataSyncSelector.confirmLastExtendedBolusIdIfGreater(pair.updateRecordId) - rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked ExtendedBolus " + ack._id)) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked ExtendedBolus " + ack._id, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedExtendedBolusesCompat() ret = Result.success(workDataOf("ProcessedData" to pair.toString())) @@ -131,7 +132,7 @@ class NSClientUpdateRemoveAckWorker( is PairProfileSwitch -> { val pair = ack.originalObject dataSyncSelector.confirmLastProfileSwitchIdIfGreater(pair.updateRecordId) - rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked ProfileSwitch " + ack._id)) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked ProfileSwitch " + ack._id, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedProfileSwitchesCompat() ret = Result.success(workDataOf("ProcessedData" to pair.toString())) @@ -140,7 +141,7 @@ class NSClientUpdateRemoveAckWorker( is PairEffectiveProfileSwitch -> { val pair = ack.originalObject dataSyncSelector.confirmLastEffectiveProfileSwitchIdIfGreater(pair.updateRecordId) - rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked EffectiveProfileSwitch " + ack._id)) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked EffectiveProfileSwitch " + ack._id, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedEffectiveProfileSwitchesCompat() ret = Result.success(workDataOf("ProcessedData" to pair.toString())) @@ -149,7 +150,7 @@ class NSClientUpdateRemoveAckWorker( is PairOfflineEvent -> { val pair = ack.originalObject dataSyncSelector.confirmLastOfflineEventIdIfGreater(pair.updateRecordId) - rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked OfflineEvent" + ack._id)) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked OfflineEvent" + ack._id, NsClient.Version.V1)) // Send new if waiting dataSyncSelector.processChangedOfflineEventsCompat() ret = Result.success(workDataOf("ProcessedData" to pair.toString())) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NsClientReceiverDelegate.kt similarity index 98% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NsClientReceiverDelegate.kt index 6c0a36aaba..3f1e92b68e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/NsClientReceiverDelegate.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient +package info.nightscout.androidaps.plugins.sync.nsclient import info.nightscout.androidaps.R import info.nightscout.androidaps.events.EventPreferenceChange diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSAddAck.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/acks/NSAddAck.kt similarity index 94% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSAddAck.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/acks/NSAddAck.kt index 043e8e21d5..46a81ea2b3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSAddAck.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/acks/NSAddAck.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient.acks +package info.nightscout.androidaps.plugins.sync.nsclient.acks import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.Event @@ -16,7 +16,7 @@ class NSAddAck( ) : Event(), Ack { var id: String? = null - var nsClientID: String? = null + private var nsClientID: String? = null var json: JSONObject? = null override fun call(vararg args: Any) { // Regular response diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSAuthAck.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/acks/NSAuthAck.kt similarity index 89% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSAuthAck.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/acks/NSAuthAck.kt index f52d2cb8e7..8e9e85f4d5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSAuthAck.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/acks/NSAuthAck.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient.acks +package info.nightscout.androidaps.plugins.sync.nsclient.acks import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.Event diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSUpdateAck.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/acks/NSUpdateAck.kt similarity index 94% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSUpdateAck.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/acks/NSUpdateAck.kt index 91856277ce..87b5673dd4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSUpdateAck.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/acks/NSUpdateAck.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient.acks +package info.nightscout.androidaps.plugins.sync.nsclient.acks import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.Event diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/AlarmAck.java b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/AlarmAck.java similarity index 72% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/AlarmAck.java rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/AlarmAck.java index 06fc06e943..f24c79ed37 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/AlarmAck.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/AlarmAck.java @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient.data; +package info.nightscout.androidaps.plugins.sync.nsclient.data; /** * Created by mike on 11.06.2017. diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSAlarm.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/NSAlarm.kt similarity index 94% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSAlarm.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/NSAlarm.kt index 81d8f0e5b1..b9741e5915 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSAlarm.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/NSAlarm.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient.data +package info.nightscout.androidaps.plugins.sync.nsclient.data import info.nightscout.interfaces.utils.JsonHelper import org.json.JSONObject diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/NSDeviceStatusHandler.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/NSDeviceStatusHandler.kt new file mode 100644 index 0000000000..8a18a1d423 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/NSDeviceStatusHandler.kt @@ -0,0 +1,173 @@ +package info.nightscout.androidaps.plugins.sync.nsclient.data + +import info.nightscout.androidaps.R +import info.nightscout.androidaps.interfaces.NsClient +import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.utils.HtmlHelper +import info.nightscout.interfaces.utils.JsonHelper +import info.nightscout.sdk.remotemodel.RemoteDeviceStatus +import info.nightscout.shared.sharedPreferences.SP +import javax.inject.Inject +import javax.inject.Singleton + +/* +{ + "_id": "594fdcec327b83c81b6b8c0f", + "device": "openaps://Sony D5803", + "pump": { + "battery": { + "percent": 100 + }, + "status": { + "status": "normal", + "timestamp": "2017-06-25T15:50:14Z" + }, + "extended": { + "Version": "1.5-ac98852-2017.06.25", + "PumpIOB": 1.13, + "LastBolus": "25. 6. 2017 17:25:00", + "LastBolusAmount": 0.3, + "BaseBasalRate": 0.4, + "ActiveProfile": "2016 +30%" + }, + "reservoir": 109, + "clock": "2017-06-25T15:55:10Z" + }, + "openaps": { + "suggested": { + "temp": "absolute", + "bg": 115.9, + "tick": "+5", + "eventualBG": 105, + "snoozeBG": 105, + "predBGs": { + "IOB": [116, 114, 112, 110, 109, 107, 106, 105, 105, 104, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 106, 106, 106, 106, 106, 107] + }, + "sensitivityRatio": 0.81, + "variable_sens": 137.3, + "COB": 0, + "IOB": -0.035, + "reason": "COB: 0, Dev: -18, BGI: 0.43, ISF: 216, Target: 99; Eventual BG 105 > 99 but Min. Delta -2.60 < Exp. Delta 0.1; setting current basal of 0.4 as temp. Suggested rate is same as profile rate, no temp basal is active, doing nothing", + "timestamp": "2017-06-25T15:55:10Z" + }, + "iob": { + "iob": -0.035, + "basaliob": -0.035, + "activity": -0.0004, + "time": "2017-06-25T15:55:10Z" + } + }, + "uploaderBattery": 93, + "created_at": "2017-06-25T15:55:10Z", + "NSCLIENT_ID": 1498406118857 +} + */ +@Suppress("SpellCheckingInspection") +@Singleton +class NSDeviceStatusHandler @Inject constructor( + private val sp: SP, + private val config: Config, + private val dateUtil: DateUtil, + private val runningConfiguration: RunningConfiguration, + private val processedDeviceStatusData: ProcessedDeviceStatusData +) { + + fun handleNewData(deviceStatuses: Array, version: NsClient.Version) { + var configurationDetected = false + for (i in deviceStatuses.size - 1 downTo 0) { + val nsDeviceStatus = deviceStatuses[i] + updatePumpData(nsDeviceStatus) + updateDeviceData(nsDeviceStatus) + updateOpenApsData(nsDeviceStatus) + updateUploaderData(nsDeviceStatus) + nsDeviceStatus.pump?.let { sp.putBoolean(R.string.key_ObjectivespumpStatusIsAvailableInNS, true) } // Objective 0 + if (config.NSCLIENT && !configurationDetected) + nsDeviceStatus.configuration?.let { + // copy configuration of Insulin and Sensitivity from main AAPS + runningConfiguration.apply(it, version) + configurationDetected = true // pick only newest + } + } + } + + private fun updateDeviceData(deviceStatus: RemoteDeviceStatus) { + val createdAt = deviceStatus.createdAt?.let { dateUtil.fromISODateString(it) } ?: return + processedDeviceStatusData.device?.let { if (createdAt < it.createdAt) return } // take only newer record + deviceStatus.device?.let { + if (it.startsWith("openaps://")) processedDeviceStatusData.device = ProcessedDeviceStatusData.Device(createdAt, it.substring(10)) + } + } + + private fun updatePumpData(remoteDeviceStatus: RemoteDeviceStatus) { + val pump = remoteDeviceStatus.pump ?: return + val clock = pump.clock?.let { dateUtil.fromISODateString(it) } ?: return + processedDeviceStatusData.pumpData?.let { if (clock < it.clock) return } // take only newer record + + // create new status and process data + processedDeviceStatusData.pumpData = ProcessedDeviceStatusData.PumpData().also { deviceStatusPumpData -> + deviceStatusPumpData.clock = clock + pump.status?.status?.let { deviceStatusPumpData.status = it } + pump.reservoir?.let { deviceStatusPumpData.reservoir = it } + pump.reservoirDisplayOverride?.let { deviceStatusPumpData.reservoirDisplayOverride = it } + pump.battery?.percent?.let { + deviceStatusPumpData.isPercent = true + deviceStatusPumpData.percent = it + } + pump.battery?.voltage?.let { + deviceStatusPumpData.isPercent = false + deviceStatusPumpData.voltage = it + } + pump.extended?.let { + val extended = StringBuilder() + val keys: Iterator<*> = it.keys() + while (keys.hasNext()) { + val key = keys.next() as String + val value = it.getString(key) + extended.append("").append(key).append(": ").append(value).append("
") + } + deviceStatusPumpData.extended = HtmlHelper.fromHtml(extended.toString()) + deviceStatusPumpData.activeProfileName = JsonHelper.safeGetStringAllowNull(it, "ActiveProfile", null) + } + } + } + + private fun updateOpenApsData(remoteDeviceStatus: RemoteDeviceStatus) { + remoteDeviceStatus.openaps?.suggested?.let { + JsonHelper.safeGetString(it, "timestamp")?.let { timestamp -> + val clock = dateUtil.fromISODateString(timestamp) + // check if this is new data + if (clock > processedDeviceStatusData.openAPSData.clockSuggested) { + processedDeviceStatusData.openAPSData.suggested = it + processedDeviceStatusData.openAPSData.clockSuggested = clock + } + } + } + remoteDeviceStatus.openaps?.enacted?.let { + JsonHelper.safeGetString(it, "timestamp")?.let { timestamp -> + val clock = dateUtil.fromISODateString(timestamp) + // check if this is new data + if (clock > processedDeviceStatusData.openAPSData.clockEnacted) { + processedDeviceStatusData.openAPSData.enacted = it + processedDeviceStatusData.openAPSData.clockEnacted = clock + } + } + } + } + + private fun updateUploaderData(remoteDeviceStatus: RemoteDeviceStatus) { + val clock = remoteDeviceStatus.createdAt?.let { dateUtil.fromISODateString(it) } ?: return + val device = remoteDeviceStatus.device ?: return + val battery = remoteDeviceStatus.uploaderBattery ?: remoteDeviceStatus.uploader?.battery ?: return + + var uploader = processedDeviceStatusData.uploaderMap[device] + // check if this is new data + if (uploader == null || clock > uploader.clock) { + if (uploader == null) uploader = ProcessedDeviceStatusData.Uploader() + uploader.battery = battery + uploader.clock = clock + processedDeviceStatusData.uploaderMap[device] = uploader + } + } +} \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSMbg.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/NSMbg.kt similarity index 86% rename from core/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSMbg.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/NSMbg.kt index 063420e272..90d0aae931 100644 --- a/core/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSMbg.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/NSMbg.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient.data +package info.nightscout.androidaps.plugins.sync.nsclient.data import info.nightscout.interfaces.utils.JsonHelper import org.json.JSONObject diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSSettingsStatus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/NSSettingsStatus.kt similarity index 97% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSSettingsStatus.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/NSSettingsStatus.kt index 7913034f54..a9686fff86 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSSettingsStatus.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/NSSettingsStatus.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient.data +package info.nightscout.androidaps.plugins.sync.nsclient.data import android.content.Context import info.nightscout.androidaps.R @@ -113,7 +113,6 @@ import javax.inject.Singleton "activeProfile": "2016 +30%" } */ -@Suppress("SpellCheckingInspection") @OpenForTesting @Singleton class NSSettingsStatus @Inject constructor( @@ -234,11 +233,6 @@ class NSSettingsStatus @Inject constructor( fun pumpExtendedSettingsFields(): String = JsonHelper.safeGetString(extendedPumpSettings(), "fields", "") - fun openAPSEnabledAlerts(): Boolean { - val openaps = JsonHelper.safeGetJSONObject(getExtendedSettings(), "openaps", null) - return JsonHelper.safeGetBoolean(openaps, "enableAlerts") - } - fun copyStatusLightsNsSettings(context: Context?) { val action = Runnable { getExtendedWarnValue("cage", "warn")?.let { sp.putDouble(R.string.key_statuslights_cage_warning, it) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSSgv.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/NSSgv.kt similarity index 94% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSSgv.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/NSSgv.kt index c9d6208940..9da2da982a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSSgv.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/NSSgv.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient.data +package info.nightscout.androidaps.plugins.sync.nsclient.data import info.nightscout.interfaces.utils.JsonHelper import org.json.JSONObject diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/ProcessedDeviceStatusData.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/ProcessedDeviceStatusData.kt new file mode 100644 index 0000000000..4f69f66db6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/data/ProcessedDeviceStatusData.kt @@ -0,0 +1,211 @@ +package info.nightscout.androidaps.plugins.sync.nsclient.data + +import android.text.Spanned +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.R +import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.androidaps.plugins.aps.loop.APSResult +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.T +import info.nightscout.interfaces.utils.HtmlHelper +import info.nightscout.interfaces.utils.JsonHelper +import info.nightscout.interfaces.utils.Round +import info.nightscout.shared.sharedPreferences.SP +import org.json.JSONObject +import javax.inject.Inject +import javax.inject.Singleton + +@Suppress("SpellCheckingInspection") +@Singleton +class ProcessedDeviceStatusData @Inject constructor( + private val rh: ResourceHelper, + private val dateUtil: DateUtil, + private val sp: SP +) { + + enum class Levels(val level: Int) { + + URGENT(2), + WARN(1), + INFO(0); + + fun toColor(): String = + when (level) { + INFO.level -> "white" + WARN.level -> "yellow" + URGENT.level -> "red" + else -> "white" + } + } + + class PumpData { + + var clock = 0L + var isPercent = false + var percent = 0 + var voltage = 0.0 + var status = "N/A" + var reservoir = 0.0 + var reservoirDisplayOverride = "" + var extended: Spanned? = null + var activeProfileName: String? = null + } + + var pumpData: PumpData? = null + + data class Device( + val createdAt: Long, + val device: String? + ) + + var device: Device? = null + + class Uploader { + + var clock = 0L + var battery = 0 + } + + val uploaderMap = HashMap() + + class OpenAPSData { + + var clockSuggested = 0L + var clockEnacted = 0L + var suggested: JSONObject? = null + var enacted: JSONObject? = null + } + + var openAPSData = OpenAPSData() + + // test warning level // color + fun pumpStatus(nsSettingsStatus: NSSettingsStatus): Spanned { + val pumpData = pumpData ?: return HtmlHelper.fromHtml("") + + //String[] ALL_STATUS_FIELDS = {"reservoir", "battery", "clock", "status", "device"}; + val string = StringBuilder() + .append("") + .append(rh.gs(R.string.pump)) + .append(": ") + + // test warning level + val level = when { + pumpData.clock + nsSettingsStatus.extendedPumpSettings("urgentClock") * 60 * 1000L < dateUtil.now() -> Levels.URGENT + pumpData.reservoir < nsSettingsStatus.extendedPumpSettings("urgentRes") -> Levels.URGENT + pumpData.isPercent && pumpData.percent < nsSettingsStatus.extendedPumpSettings("urgentBattP") -> Levels.URGENT + !pumpData.isPercent && pumpData.voltage > 0 && pumpData.voltage < nsSettingsStatus.extendedPumpSettings("urgentBattV") -> Levels.URGENT + pumpData.clock + nsSettingsStatus.extendedPumpSettings("warnClock") * 60 * 1000L < dateUtil.now() -> Levels.WARN + pumpData.reservoir < nsSettingsStatus.extendedPumpSettings("warnRes") -> Levels.WARN + pumpData.isPercent && pumpData.percent < nsSettingsStatus.extendedPumpSettings("warnBattP") -> Levels.WARN + !pumpData.isPercent && pumpData.voltage > 0 && pumpData.voltage < nsSettingsStatus.extendedPumpSettings("warnBattV") -> Levels.WARN + else -> Levels.INFO + } + string.append("") + val insulinUnit = rh.gs(R.string.insulin_unit_shortname) + val fields = nsSettingsStatus.pumpExtendedSettingsFields() + if (pumpData.reservoirDisplayOverride != "") + string.append(pumpData.reservoirDisplayOverride).append("$insulinUnit ") + else if (fields.contains("reservoir")) string.append(pumpData.reservoir.toInt()).append("$insulinUnit ") + if (fields.contains("battery") && pumpData.isPercent) string.append(pumpData.percent).append("% ") + if (fields.contains("battery") && !pumpData.isPercent) string.append(Round.roundTo(pumpData.voltage, 0.001)).append(" ") + if (fields.contains("clock")) string.append(dateUtil.minAgo(rh, pumpData.clock)).append(" ") + if (fields.contains("status")) string.append(pumpData.status).append(" ") + if (fields.contains("device")) string.append(device).append(" ") + string.append("") // color + return HtmlHelper.fromHtml(string.toString()) + } + + val extendedPumpStatus: Spanned get() = pumpData?.extended ?: HtmlHelper.fromHtml("") + val extendedOpenApsStatus: Spanned + get() { + val string = StringBuilder() + val enacted = openAPSData.enacted + val suggested = openAPSData.suggested + if (enacted != null && openAPSData.clockEnacted != openAPSData.clockSuggested) string + .append("") + .append(dateUtil.minAgo(rh, openAPSData.clockEnacted)) + .append(" ") + .append(JsonHelper.safeGetString(enacted, "reason")) + .append("
") + if (suggested != null) string + .append("") + .append(dateUtil.minAgo(rh, openAPSData.clockSuggested)) + .append(" ") + .append(JsonHelper.safeGetString(suggested, "reason")) + .append("
") + return HtmlHelper.fromHtml(string.toString()) + } + + val openApsStatus: Spanned + get() { + val string = StringBuilder() + .append("") + .append(rh.gs(R.string.openaps_short)) + .append(": ") + + // test warning level + val level = when { + openAPSData.clockSuggested + T.mins(sp.getLong(R.string.key_nsalarm_urgent_staledatavalue, 31)).msecs() < dateUtil.now() -> Levels.URGENT + openAPSData.clockSuggested + T.mins(sp.getLong(R.string.key_nsalarm_staledatavalue, 16)).msecs() < dateUtil.now() -> Levels.WARN + else -> Levels.INFO + } + string.append("") + if (openAPSData.clockSuggested != 0L) string.append(dateUtil.minAgo(rh, openAPSData.clockSuggested)).append(" ") + string.append("") // color + return HtmlHelper.fromHtml(string.toString()) + } + + val openApsTimestamp: Long + get() = if (openAPSData.clockSuggested != 0L) openAPSData.clockSuggested else -1 + + fun getAPSResult(injector: HasAndroidInjector): APSResult { + val result = APSResult(injector) + result.json = openAPSData.suggested + result.date = openAPSData.clockSuggested + return result + } + val uploaderStatus: String + get() { + val iterator: Iterator<*> = uploaderMap.entries.iterator() + var minBattery = 100 + while (iterator.hasNext()) { + val pair = iterator.next() as Map.Entry<*, *> + val uploader = pair.value as Uploader + if (minBattery > uploader.battery) minBattery = uploader.battery + } + return "$minBattery%" + } + + val uploaderStatusSpanned: Spanned + get() { + val string = StringBuilder() + string.append("") + string.append(rh.gs(R.string.uploader_short)) + string.append(": ") + val iterator: Iterator<*> = uploaderMap.entries.iterator() + var minBattery = 100 + while (iterator.hasNext()) { + val pair = iterator.next() as Map.Entry<*, *> + val uploader = pair.value as Uploader + if (minBattery > uploader.battery) minBattery = uploader.battery + } + string.append(minBattery) + string.append("%") + return HtmlHelper.fromHtml(string.toString()) + } + + val extendedUploaderStatus: Spanned + get() { + val string = StringBuilder() + val iterator: Iterator<*> = uploaderMap.entries.iterator() + while (iterator.hasNext()) { + val pair = iterator.next() as Map.Entry<*, *> + val uploader = pair.value as Uploader + val device = pair.key as String + string.append("").append(device).append(": ").append(uploader.battery).append("%
") + } + return HtmlHelper.fromHtml(string.toString()) + } + +} + diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/services/NSClientService.kt similarity index 80% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/services/NSClientService.kt index 50c9dcd960..ed2e809482 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclient/services/NSClientService.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient.services +package info.nightscout.androidaps.plugins.sync.nsclient.services import android.annotation.SuppressLint import android.content.Context @@ -12,42 +12,47 @@ import android.os.SystemClock import androidx.work.OneTimeWorkRequest import com.google.common.base.Charsets import com.google.common.hash.Hashing +import com.google.gson.GsonBuilder +import com.google.gson.JsonDeserializer import dagger.android.DaggerService import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.events.EventPreferenceChange -import info.nightscout.interfaces.BuildHelper -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.DataSyncSelector +import info.nightscout.androidaps.interfaces.NsClient import info.nightscout.androidaps.interfaces.ResourceHelper -import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddAckWorker -import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddUpdateWorker -import info.nightscout.androidaps.plugins.general.nsclient.NSClientMbgWorker -import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin -import info.nightscout.androidaps.plugins.general.nsclient.NSClientUpdateRemoveAckWorker -import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAddAck -import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAuthAck -import info.nightscout.androidaps.plugins.general.nsclient.acks.NSUpdateAck -import info.nightscout.androidaps.plugins.general.nsclient.data.AlarmAck -import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm -import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus -import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientUpdateGUI import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification -import info.nightscout.interfaces.notifications.Notification import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction -import info.nightscout.androidaps.plugins.source.NSClientSourcePlugin.NSClientSourceWorker +import info.nightscout.androidaps.plugins.source.NSClientSourcePlugin +import info.nightscout.androidaps.plugins.sync.nsShared.StoreDataForDb +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientNewLog +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientStatus +import info.nightscout.androidaps.plugins.sync.nsclient.NSClientAddAckWorker +import info.nightscout.androidaps.plugins.sync.nsclient.NSClientAddUpdateWorker +import info.nightscout.androidaps.plugins.sync.nsclient.NSClientMbgWorker +import info.nightscout.androidaps.plugins.sync.nsclient.NSClientPlugin +import info.nightscout.androidaps.plugins.sync.nsclient.NSClientUpdateRemoveAckWorker +import info.nightscout.androidaps.plugins.sync.nsclient.acks.NSAddAck +import info.nightscout.androidaps.plugins.sync.nsclient.acks.NSAuthAck +import info.nightscout.androidaps.plugins.sync.nsclient.acks.NSUpdateAck +import info.nightscout.androidaps.plugins.sync.nsclient.data.AlarmAck +import info.nightscout.androidaps.plugins.sync.nsclient.data.NSAlarm +import info.nightscout.androidaps.plugins.sync.nsclient.data.NSDeviceStatusHandler +import info.nightscout.androidaps.plugins.sync.nsclient.data.NSSettingsStatus +import info.nightscout.androidaps.plugins.sync.nsclientV3.NSClientV3Plugin import info.nightscout.androidaps.receivers.DataWorkerStorage import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.T.Companion.mins +import info.nightscout.interfaces.BuildHelper +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.notifications.Notification import info.nightscout.interfaces.utils.JsonHelper.safeGetString import info.nightscout.interfaces.utils.JsonHelper.safeGetStringAllowNull -import info.nightscout.androidaps.utils.T.Companion.mins -import info.nightscout.plugins.general.food.FoodPlugin.FoodWorker +import info.nightscout.plugins.general.food.FoodPlugin import info.nightscout.plugins.profile.ProfilePlugin import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus @@ -56,6 +61,7 @@ import info.nightscout.rx.events.EventConfigBuilderChange import info.nightscout.rx.events.EventNSClientRestart import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag +import info.nightscout.sdk.remotemodel.RemoteDeviceStatus import info.nightscout.shared.sharedPreferences.SP import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign @@ -69,13 +75,13 @@ import java.net.URISyntaxException import java.util.Locale import javax.inject.Inject -class NSClientService : DaggerService() { +class NSClientService : DaggerService(), NsClient.NSClientService { @Inject lateinit var injector: HasAndroidInjector @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var nsSettingsStatus: NSSettingsStatus - @Inject lateinit var nsDeviceStatus: NSDeviceStatus + @Inject lateinit var nsDeviceStatusHandler: NSDeviceStatusHandler @Inject lateinit var rxBus: RxBus @Inject lateinit var rh: ResourceHelper @Inject lateinit var sp: SP @@ -206,13 +212,13 @@ class NSClientService : DaggerService() { connectionStatus += ')' isConnected = true hasWriteAuth = ack.write && ack.writeTreatment - rxBus.send(EventNSClientStatus(connectionStatus)) - rxBus.send(EventNSClientNewLog("AUTH", connectionStatus)) + rxBus.send(EventNSClientStatus(connectionStatus, NsClient.Version.V1)) + rxBus.send(EventNSClientNewLog("AUTH", connectionStatus, NsClient.Version.V1)) if (!ack.write) { - rxBus.send(EventNSClientNewLog("ERROR", "Write permission not granted ")) + rxBus.send(EventNSClientNewLog("ERROR", "Write permission not granted ", NsClient.Version.V1)) } if (!ack.writeTreatment) { - rxBus.send(EventNSClientNewLog("ERROR", "Write treatment permission not granted ")) + rxBus.send(EventNSClientNewLog("ERROR", "Write treatment permission not granted ", NsClient.Version.V1)) } if (!hasWriteAuth) { val noWritePerm = Notification(Notification.NSCLIENT_NO_WRITE_PERMISSION, rh.gs(R.string.nowritepermission), Notification.URGENT) @@ -235,21 +241,21 @@ class NSClientService : DaggerService() { fun initialize() { dataCounter = 0 readPreferences() - @Suppress("UnstableApiUsage", "DEPRECATION") + @Suppress("DEPRECATION") if (nsAPISecret != "") nsApiHashCode = Hashing.sha1().hashString(nsAPISecret, Charsets.UTF_8).toString() - rxBus.send(EventNSClientStatus("Initializing")) + rxBus.send(EventNSClientStatus("Initializing", NsClient.Version.V1)) if (!nsClientPlugin.isAllowed) { - rxBus.send(EventNSClientNewLog("NSCLIENT", nsClientPlugin.blockingReason)) - rxBus.send(EventNSClientStatus(nsClientPlugin.blockingReason)) - } else if (nsClientPlugin.paused) { - rxBus.send(EventNSClientNewLog("NSCLIENT", "paused")) - rxBus.send(EventNSClientStatus("Paused")) + rxBus.send(EventNSClientNewLog("NSCLIENT", nsClientPlugin.blockingReason, NsClient.Version.V1)) + rxBus.send(EventNSClientStatus(nsClientPlugin.blockingReason, NsClient.Version.V1)) + } else if (sp.getBoolean(R.string.key_nsclientinternal_paused, false)) { + rxBus.send(EventNSClientNewLog("NSCLIENT", "paused", NsClient.Version.V1)) + rxBus.send(EventNSClientStatus("Paused", NsClient.Version.V1)) } else if (!nsEnabled) { - rxBus.send(EventNSClientNewLog("NSCLIENT", "disabled")) - rxBus.send(EventNSClientStatus("Disabled")) + rxBus.send(EventNSClientNewLog("NSCLIENT", "disabled", NsClient.Version.V1)) + rxBus.send(EventNSClientStatus("Disabled", NsClient.Version.V1)) } else if (nsURL != "" && (buildHelper.isEngineeringMode() || nsURL.lowercase(Locale.getDefault()).startsWith("https://"))) { try { - rxBus.send(EventNSClientStatus("Connecting ...")) + rxBus.send(EventNSClientStatus("Connecting ...", NsClient.Version.V1)) val opt = IO.Options() opt.forceNew = true opt.reconnection = true @@ -260,7 +266,7 @@ class NSClientService : DaggerService() { socket.on(Socket.EVENT_CONNECT_ERROR, onError) socket.on(Socket.EVENT_CONNECT_TIMEOUT, onError) socket.on(Socket.EVENT_PING, onPing) - rxBus.send(EventNSClientNewLog("NSCLIENT", "do connect")) + rxBus.send(EventNSClientNewLog("NSCLIENT", "do connect", NsClient.Version.V1)) socket.connect() socket.on("dataUpdate", onDataUpdate) socket.on("announcement", onAnnouncement) @@ -269,25 +275,25 @@ class NSClientService : DaggerService() { socket.on("clear_alarm", onClearAlarm) } } catch (e: URISyntaxException) { - rxBus.send(EventNSClientNewLog("NSCLIENT", "Wrong URL syntax")) - rxBus.send(EventNSClientStatus("Wrong URL syntax")) + rxBus.send(EventNSClientNewLog("NSCLIENT", "Wrong URL syntax", NsClient.Version.V1)) + rxBus.send(EventNSClientStatus("Wrong URL syntax", NsClient.Version.V1)) } catch (e: RuntimeException) { - rxBus.send(EventNSClientNewLog("NSCLIENT", "Wrong URL syntax")) - rxBus.send(EventNSClientStatus("Wrong URL syntax")) + rxBus.send(EventNSClientNewLog("NSCLIENT", "Wrong URL syntax", NsClient.Version.V1)) + rxBus.send(EventNSClientStatus("Wrong URL syntax", NsClient.Version.V1)) } } else if (nsURL.lowercase(Locale.getDefault()).startsWith("http://")) { - rxBus.send(EventNSClientNewLog("NSCLIENT", "NS URL not encrypted")) - rxBus.send(EventNSClientStatus("Not encrypted")) + rxBus.send(EventNSClientNewLog("NSCLIENT", "NS URL not encrypted", NsClient.Version.V1)) + rxBus.send(EventNSClientStatus("Not encrypted", NsClient.Version.V1)) } else { - rxBus.send(EventNSClientNewLog("NSCLIENT", "No NS URL specified")) - rxBus.send(EventNSClientStatus("Not configured")) + rxBus.send(EventNSClientNewLog("NSCLIENT", "No NS URL specified", NsClient.Version.V1)) + rxBus.send(EventNSClientStatus("Not configured", NsClient.Version.V1)) } } private val onConnect = Emitter.Listener { connectCounter++ val socketId = socket?.id() ?: "NULL" - rxBus.send(EventNSClientNewLog("NSCLIENT", "connect #$connectCounter event. ID: $socketId")) + rxBus.send(EventNSClientNewLog("NSCLIENT", "connect #$connectCounter event. ID: $socketId", NsClient.Version.V1)) if (socket != null) sendAuthMessage(NSAuthAck(rxBus)) watchdog() } @@ -301,16 +307,16 @@ class NSClientService : DaggerService() { reconnections.remove(r) } } - rxBus.send(EventNSClientNewLog("WATCHDOG", "connections in last " + WATCHDOG_INTERVAL_MINUTES + " minutes: " + reconnections.size + "/" + WATCHDOG_MAX_CONNECTIONS)) + rxBus.send(EventNSClientNewLog("WATCHDOG", "connections in last " + WATCHDOG_INTERVAL_MINUTES + " minutes: " + reconnections.size + "/" + WATCHDOG_MAX_CONNECTIONS, NsClient.Version.V1)) if (reconnections.size >= WATCHDOG_MAX_CONNECTIONS) { val n = Notification(Notification.NS_MALFUNCTION, rh.gs(R.string.nsmalfunction), Notification.URGENT) rxBus.send(EventNewNotification(n)) - rxBus.send(EventNSClientNewLog("WATCHDOG", "pausing for $WATCHDOG_RECONNECT_IN minutes")) + rxBus.send(EventNSClientNewLog("WATCHDOG", "pausing for $WATCHDOG_RECONNECT_IN minutes", NsClient.Version.V1)) nsClientPlugin.pause(true) rxBus.send(EventNSClientUpdateGUI()) Thread { SystemClock.sleep(mins(WATCHDOG_RECONNECT_IN.toLong()).msecs()) - rxBus.send(EventNSClientNewLog("WATCHDOG", "re-enabling NSClient")) + rxBus.send(EventNSClientNewLog("WATCHDOG", "re-enabling NSClient", NsClient.Version.V1)) nsClientPlugin.pause(false) }.start() } @@ -319,7 +325,7 @@ class NSClientService : DaggerService() { private val onDisconnect = Emitter.Listener { args -> aapsLogger.debug(LTag.NSCLIENT, "disconnect reason: {}", *args) - rxBus.send(EventNSClientNewLog("NSCLIENT", "disconnect event")) + rxBus.send(EventNSClientNewLog("NSCLIENT", "disconnect event", NsClient.Version.V1)) } @Synchronized fun destroy() { @@ -331,7 +337,7 @@ class NSClientService : DaggerService() { socket?.off("alarm") socket?.off("urgent_alarm") socket?.off("clear_alarm") - rxBus.send(EventNSClientNewLog("NSCLIENT", "destroy")) + rxBus.send(EventNSClientNewLog("NSCLIENT", "destroy", NsClient.Version.V1)) isConnected = false hasWriteAuth = false socket?.disconnect() @@ -350,11 +356,11 @@ class NSClientService : DaggerService() { aapsLogger.error("Unhandled exception", e) return } - rxBus.send(EventNSClientNewLog("AUTH", "requesting auth")) + rxBus.send(EventNSClientNewLog("AUTH", "requesting auth", NsClient.Version.V1)) socket?.emit("authorize", authMessage, ack) } - fun readPreferences() { + private fun readPreferences() { nsEnabled = nsClientPlugin.isEnabled() nsURL = sp.getString(R.string.key_nsclientinternal_url, "") nsAPISecret = sp.getString(R.string.key_nsclientinternal_api_secret, "") @@ -366,10 +372,10 @@ class NSClientService : DaggerService() { if (args.isNotEmpty() && args[0] != null) { msg = args[0].toString() } - rxBus.send(EventNSClientNewLog("ERROR", msg)) + rxBus.send(EventNSClientNewLog("ERROR", msg, NsClient.Version.V1)) } private val onPing = Emitter.Listener { - rxBus.send(EventNSClientNewLog("PING", "received")) + rxBus.send(EventNSClientNewLog("PING", "received", NsClient.Version.V1)) // send data if there is something waiting resend("Ping received") } @@ -439,7 +445,7 @@ class NSClientService : DaggerService() { val data: JSONObject try { data = args[0] as JSONObject - rxBus.send(EventNSClientNewLog("CLEARALARM", "received")) + rxBus.send(EventNSClientNewLog("CLEARALARM", "received", NsClient.Version.V1)) rxBus.send(EventDismissNotification(Notification.NS_ALARM)) rxBus.send(EventDismissNotification(Notification.NS_URGENT_ALARM)) aapsLogger.debug(LTag.NSCLIENT, data.toString()) @@ -458,19 +464,19 @@ class NSClientService : DaggerService() { try { // delta means only increment/changes are coming val isDelta = data.has("delta") - rxBus.send(EventNSClientNewLog("DATA", "Data packet #" + dataCounter++ + if (isDelta) " delta" else " full")) + rxBus.send(EventNSClientNewLog("DATA", "Data packet #" + dataCounter++ + if (isDelta) " delta" else " full", NsClient.Version.V1)) if (data.has("status")) { val status = data.getJSONObject("status") nsSettingsStatus.handleNewData(status) } else if (!isDelta) { - rxBus.send(EventNSClientNewLog("ERROR", "Unsupported Nightscout version ")) + rxBus.send(EventNSClientNewLog("ERROR", "Unsupported Nightscout version ", NsClient.Version.V1)) } if (data.has("profiles")) { val profiles = data.getJSONArray("profiles") if (profiles.length() > 0) { // take the newest val profileStoreJson = profiles[profiles.length() - 1] as JSONObject - rxBus.send(EventNSClientNewLog("PROFILE", "profile received")) + rxBus.send(EventNSClientNewLog("PROFILE", "profile received", NsClient.Version.V1)) dataWorkerStorage.enqueue( OneTimeWorkRequest.Builder(ProfilePlugin.NSProfileWorker::class.java) .setInputData(dataWorkerStorage.storeInputData(profileStoreJson)) @@ -481,7 +487,7 @@ class NSClientService : DaggerService() { if (data.has("treatments")) { val treatments = data.getJSONArray("treatments") val addedOrUpdatedTreatments = JSONArray() - if (treatments.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + treatments.length() + " treatments")) + if (treatments.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + treatments.length() + " treatments", NsClient.Version.V1)) for (index in 0 until treatments.length()) { val jsonTreatment = treatments.getJSONObject(index) val action = safeGetStringAllowNull(jsonTreatment, "action", null) @@ -497,24 +503,31 @@ class NSClientService : DaggerService() { } } if (data.has("devicestatus")) { - val devicestatuses = data.getJSONArray("devicestatus") - if (devicestatuses.length() > 0) { - rxBus.send(EventNSClientNewLog("DATA", "received " + devicestatuses.length() + " device statuses")) - nsDeviceStatus.handleNewData(devicestatuses) + val deserializer: JsonDeserializer = + JsonDeserializer { json, _, _ -> + JSONObject(json.asJsonObject.toString()) + } + val gson = GsonBuilder().also { + it.registerTypeAdapter(JSONObject::class.java, deserializer) + }.create() + val devicestatuses = gson.fromJson(data.getString("devicestatus"), Array::class.java) + if (devicestatuses.isNotEmpty()) { + rxBus.send(EventNSClientNewLog("DATA", "received " + devicestatuses.size + " device statuses", NsClient.Version.V1)) + nsDeviceStatusHandler.handleNewData(devicestatuses, NsClient.Version.V1) } } if (data.has("food")) { val foods = data.getJSONArray("food") - if (foods.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + foods.length() + " foods")) + if (foods.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + foods.length() + " foods", NsClient.Version.V1)) dataWorkerStorage.enqueue( - OneTimeWorkRequest.Builder(FoodWorker::class.java) + OneTimeWorkRequest.Builder(FoodPlugin.FoodWorker::class.java) .setInputData(dataWorkerStorage.storeInputData(foods)) .build() ) } if (data.has("mbgs")) { val mbgArray = data.getJSONArray("mbgs") - if (mbgArray.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + mbgArray.length() + " mbgs")) + if (mbgArray.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + mbgArray.length() + " mbgs", NsClient.Version.V1)) dataWorkerStorage.enqueue( OneTimeWorkRequest.Builder(NSClientMbgWorker::class.java) .setInputData(dataWorkerStorage.storeInputData(mbgArray)) @@ -523,23 +536,26 @@ class NSClientService : DaggerService() { } if (data.has("cals")) { val cals = data.getJSONArray("cals") - if (cals.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + cals.length() + " cals")) + if (cals.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + cals.length() + " cals", NsClient.Version.V1)) // Calibrations ignored } if (data.has("sgvs")) { val sgvs = data.getJSONArray("sgvs") if (sgvs.length() > 0) { - rxBus.send(EventNSClientNewLog("DATA", "received " + sgvs.length() + " sgvs")) + rxBus.send(EventNSClientNewLog("DATA", "received " + sgvs.length() + " sgvs", NsClient.Version.V1)) // Objective0 sp.putBoolean(R.string.key_ObjectivesbgIsAvailableInNS, true) - dataWorkerStorage.enqueue( - OneTimeWorkRequest.Builder(NSClientSourceWorker::class.java) - .setInputData(dataWorkerStorage.storeInputData(sgvs)) - .build() - ) + dataWorkerStorage + .beginUniqueWork( + NSClientV3Plugin.JOB_NAME, + OneTimeWorkRequest.Builder(NSClientSourcePlugin.NSClientSourceWorker::class.java) + .setInputData(dataWorkerStorage.storeInputData(sgvs)) + .build() + ).then(OneTimeWorkRequest.Builder(StoreDataForDb.StoreBgWorker::class.java).build()) + .enqueue() } } - rxBus.send(EventNSClientNewLog("LAST", dateUtil.dateAndTimeString(latestDateInReceivedData))) + rxBus.send(EventNSClientNewLog("LAST", dateUtil.dateAndTimeString(latestDateInReceivedData), NsClient.Version.V1)) } catch (e: JSONException) { aapsLogger.error("Unhandled exception", e) } @@ -550,7 +566,7 @@ class NSClientService : DaggerService() { } } - fun dbUpdate(collection: String, _id: String?, data: JSONObject?, originalObject: Any, progress: String) { + override fun dbUpdate(collection: String, _id: String?, data: JSONObject?, originalObject: Any, progress: String) { try { if (_id == null) return if (!isConnected || !hasWriteAuth) return @@ -562,7 +578,7 @@ class NSClientService : DaggerService() { rxBus.send( EventNSClientNewLog( "DBUPDATE $collection", "Sent " + originalObject.javaClass.simpleName + " " + - "" + _id + " " + data + progress + "" + _id + " " + data + progress, NsClient.Version.V1 ) ) } catch (e: JSONException) { @@ -570,14 +586,14 @@ class NSClientService : DaggerService() { } } - fun dbAdd(collection: String, data: JSONObject, originalObject: Any, progress: String) { + override fun dbAdd(collection: String, data: JSONObject, originalObject: Any, progress: String) { try { if (!isConnected || !hasWriteAuth) return val message = JSONObject() message.put("collection", collection) message.put("data", data) socket?.emit("dbAdd", message, NSAddAck(aapsLogger, rxBus, originalObject)) - rxBus.send(EventNSClientNewLog("DBADD $collection", "Sent " + originalObject.javaClass.simpleName + " " + data + " " + progress)) + rxBus.send(EventNSClientNewLog("DBADD $collection", "Sent " + originalObject.javaClass.simpleName + " " + data + " " + progress, NsClient.Version.V1)) } catch (e: JSONException) { aapsLogger.error("Unhandled exception", e) } @@ -586,7 +602,7 @@ class NSClientService : DaggerService() { fun sendAlarmAck(alarmAck: AlarmAck) { if (!isConnected || !hasWriteAuth) return socket?.emit("ack", alarmAck.level, alarmAck.group, alarmAck.silenceTime) - rxBus.send(EventNSClientNewLog("ALARMACK ", alarmAck.level.toString() + " " + alarmAck.group + " " + alarmAck.silenceTime)) + rxBus.send(EventNSClientNewLog("ALARMACK ", alarmAck.level.toString() + " " + alarmAck.group + " " + alarmAck.silenceTime, NsClient.Version.V1)) } fun resend(reason: String) { @@ -602,9 +618,9 @@ class NSClientService : DaggerService() { // "AndroidAPS:NSClientService_onDataUpdate") // wakeLock.acquire(mins(10).msecs()) try { - rxBus.send(EventNSClientNewLog("QUEUE", "Resend started: $reason")) + rxBus.send(EventNSClientNewLog("QUEUE", "Resend started: $reason", NsClient.Version.V1)) dataSyncSelector.doUpload() - rxBus.send(EventNSClientNewLog("QUEUE", "Resend ended: $reason")) + rxBus.send(EventNSClientNewLog("QUEUE", "Resend ended: $reason", NsClient.Version.V1)) } finally { // if (wakeLock.isHeld) wakeLock.release() } @@ -622,7 +638,7 @@ class NSClientService : DaggerService() { val nsAlarm = NSAlarm(announcement) val notification: Notification = NotificationWithAction(injector, nsAlarm) rxBus.send(EventNewNotification(notification)) - rxBus.send(EventNSClientNewLog("ANNOUNCEMENT", safeGetString(announcement, "message", "received"))) + rxBus.send(EventNSClientNewLog("ANNOUNCEMENT", safeGetString(announcement, "message", "received"), NsClient.Version.V1)) aapsLogger.debug(LTag.NSCLIENT, announcement.toString()) } } @@ -636,7 +652,7 @@ class NSClientService : DaggerService() { val notification: Notification = NotificationWithAction(injector, nsAlarm) rxBus.send(EventNewNotification(notification)) } - rxBus.send(EventNSClientNewLog("ALARM", safeGetString(alarm, "message", "received"))) + rxBus.send(EventNSClientNewLog("ALARM", safeGetString(alarm, "message", "received"), NsClient.Version.V1)) aapsLogger.debug(LTag.NSCLIENT, alarm.toString()) } } @@ -650,7 +666,7 @@ class NSClientService : DaggerService() { val notification: Notification = NotificationWithAction(injector, nsAlarm) rxBus.send(EventNewNotification(notification)) } - rxBus.send(EventNSClientNewLog("URGENTALARM", safeGetString(alarm, "message", "received"))) + rxBus.send(EventNSClientNewLog("URGENTALARM", safeGetString(alarm, "message", "received"), NsClient.Version.V1)) aapsLogger.debug(LTag.NSCLIENT, alarm.toString()) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/NSClientV3Plugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/NSClientV3Plugin.kt new file mode 100644 index 0000000000..3aeed76751 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/NSClientV3Plugin.kt @@ -0,0 +1,316 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3 + +import android.content.Context +import android.os.Handler +import android.os.HandlerThread +import android.text.Spanned +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.PreferenceScreen +import androidx.preference.SwitchPreference +import androidx.work.ExistingWorkPolicy +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkInfo +import androidx.work.WorkManager +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.R +import info.nightscout.androidaps.events.EventPreferenceChange +import info.nightscout.androidaps.interfaces.NsClient +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.androidaps.interfaces.Sync +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientUpdateGUI +import info.nightscout.androidaps.plugins.sync.nsShared.NSClientFragment +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientNewLog +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientResend +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientStatus +import info.nightscout.androidaps.plugins.sync.nsclient.NsClientReceiverDelegate +import info.nightscout.androidaps.plugins.sync.nsclient.data.AlarmAck +import info.nightscout.androidaps.plugins.sync.nsclient.data.NSAlarm +import info.nightscout.androidaps.plugins.sync.nsclient.services.NSClientService +import info.nightscout.androidaps.plugins.sync.nsclientV3.workers.LoadBgWorker +import info.nightscout.androidaps.plugins.sync.nsclientV3.workers.LoadLastModificationWorker +import info.nightscout.androidaps.plugins.sync.nsclientV3.workers.LoadStatusWorker +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.ToastUtils +import info.nightscout.interfaces.BuildHelper +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.Constants +import info.nightscout.interfaces.PluginDescription +import info.nightscout.interfaces.PluginType +import info.nightscout.interfaces.utils.HtmlHelper +import info.nightscout.rx.AapsSchedulers +import info.nightscout.rx.bus.RxBus +import info.nightscout.rx.events.EventChargingState +import info.nightscout.rx.events.EventNetworkChange +import info.nightscout.rx.logging.AAPSLogger +import info.nightscout.rx.logging.LTag +import info.nightscout.sdk.NSAndroidClientImpl +import info.nightscout.sdk.interfaces.NSAndroidClient +import info.nightscout.sdk.remotemodel.LastModified +import info.nightscout.shared.sharedPreferences.SP +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.math.max + +@Singleton +class NSClientV3Plugin @Inject constructor( + injector: HasAndroidInjector, + aapsLogger: AAPSLogger, + private val aapsSchedulers: AapsSchedulers, + private val rxBus: RxBus, + rh: ResourceHelper, + private val context: Context, + private val fabricPrivacy: FabricPrivacy, + private val sp: SP, + private val nsClientReceiverDelegate: NsClientReceiverDelegate, + private val config: Config, + private val buildHelper: BuildHelper, + private val dateUtil: DateUtil +) : NsClient, Sync, PluginBase( + PluginDescription() + .mainType(PluginType.SYNC) + .fragmentClass(NSClientFragment::class.java.name) + .pluginIcon(R.drawable.ic_nightscout_syncs) + .pluginName(R.string.nsclientv3) + .shortName(R.string.nsclientv3_shortname) + .preferencesId(R.xml.pref_nsclientinternal) + .description(R.string.description_ns_client_v3), + aapsLogger, rh, injector +) { + + companion object { + + val JOB_NAME: String = this::class.java.simpleName + } + + private val disposable = CompositeDisposable() + private val handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) + private val listLog: MutableList = ArrayList() + override var status = "" + override val nsClientService: NSClientService? = null // service not needed + + internal lateinit var nsAndroidClient: NSAndroidClient +// private lateinit var nsAndroidRxClient: NSAndroidRxClient + + val isAllowed: Boolean + get() = nsClientReceiverDelegate.allowed + val blockingReason: String + get() = nsClientReceiverDelegate.blockingReason + + private val maxAge = T.days(77).msecs() + internal var lastModified: LastModified? = null // timestamp of last modification for every collection + internal var lastFetched = + LastModified( + LastModified.Collections( + dateUtil.now() - maxAge, + dateUtil.now() - maxAge, + dateUtil.now() - maxAge, + dateUtil.now() - maxAge + ) + ) // timestamp of last fetched data for every collection + + override fun onStart() { +// context.bindService(Intent(context, NSClientService::class.java), mConnection, Context.BIND_AUTO_CREATE) + super.onStart() + + lastFetched = Json.decodeFromString( + sp.getString( + R.string.key_nsclientv2_lastmodified, + Json.encodeToString( + LastModified.serializer(), + LastModified(LastModified.Collections(dateUtil.now() - maxAge, dateUtil.now() - maxAge, dateUtil.now() - maxAge, dateUtil.now() - maxAge)) + ) + ) + ) + lastFetched.collections.entries = max(dateUtil.now() - maxAge, lastFetched.collections.entries) + lastFetched.collections.treatments = max(dateUtil.now() - maxAge, lastFetched.collections.treatments) + lastFetched.collections.profile = max(dateUtil.now() - maxAge, lastFetched.collections.profile) + lastFetched.collections.devicestatus = max(dateUtil.now() - maxAge, lastFetched.collections.devicestatus) + + nsAndroidClient = NSAndroidClientImpl( + baseUrl = sp.getString(R.string.key_nsclientinternal_url, "").lowercase().replace("https://", ""), + accessToken = sp.getString(R.string.key_nsclient_token, ""), + context = context, + logging = true + ) + + nsClientReceiverDelegate.grabReceiversState() + disposable += rxBus + .toObservable(EventNSClientStatus::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ event -> + if (event.version == NsClient.Version.V3) { + status = event.getStatus(rh) + rxBus.send(EventNSClientUpdateGUI()) + } + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventNetworkChange::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ ev -> nsClientReceiverDelegate.onStatusEvent(ev) }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventPreferenceChange::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ ev -> nsClientReceiverDelegate.onStatusEvent(ev) }, fabricPrivacy::logException) + // disposable += rxBus + // .toObservable(EventAppExit::class.java) + // .observeOn(aapsSchedulers.io) + // .subscribe({ if (nsClientService != null) context.unbindService(mConnection) }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventNSClientNewLog::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ event -> + if (event.version != NsClient.Version.V3) return@subscribe + addToLog(event) + aapsLogger.debug(LTag.NSCLIENT, event.action + " " + event.logText) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventChargingState::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ ev -> nsClientReceiverDelegate.onStatusEvent(ev) }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventNSClientResend::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ event -> resend(event.reason) }, fabricPrivacy::logException) + } + + override fun onStop() { + // context.applicationContext.unbindService(mConnection) + disposable.clear() + super.onStop() + } + + override fun preprocessPreferences(preferenceFragment: PreferenceFragmentCompat) { + super.preprocessPreferences(preferenceFragment) + if (config.NSCLIENT) { + preferenceFragment.findPreference(rh.gs(R.string.ns_sync_options))?.isVisible = false + + preferenceFragment.findPreference(rh.gs(R.string.key_ns_create_announcements_from_errors))?.isVisible = false + preferenceFragment.findPreference(rh.gs(R.string.key_ns_create_announcements_from_carbs_req))?.isVisible = false + } + preferenceFragment.findPreference(rh.gs(R.string.key_ns_receive_tbr_eb))?.isVisible = buildHelper.isEngineeringMode() + } + + override val hasWritePermission: Boolean get() = nsClientService?.hasWriteAuth ?: false + override val connected: Boolean get() = nsClientService?.isConnected ?: false + + override fun clearLog() { + handler.post { + synchronized(listLog) { listLog.clear() } + rxBus.send(EventNSClientUpdateGUI()) + } + } + + private fun addToLog(ev: EventNSClientNewLog) { + synchronized(listLog) { + listLog.add(ev) + // remove the first line if log is too large + if (listLog.size >= Constants.MAX_LOG_LINES) { + listLog.removeAt(0) + } + } + rxBus.send(EventNSClientUpdateGUI()) + } + + override fun textLog(): Spanned { + try { + val newTextLog = StringBuilder() + synchronized(listLog) { + for (log in listLog) newTextLog.append(log.toPreparedHtml()) + } + return HtmlHelper.fromHtml(newTextLog.toString()) + } catch (e: OutOfMemoryError) { + ToastUtils.showToastInUiThread(context, rxBus, "Out of memory!\nStop using this phone !!!", R.raw.error) + } + return HtmlHelper.fromHtml("") + } + + override fun resend(reason: String) { + nsClientService?.resend(reason) + } + + override fun pause(newState: Boolean) { + sp.putBoolean(R.string.key_nsclientinternal_paused, newState) + rxBus.send(EventPreferenceChange(rh, R.string.key_nsclientinternal_paused)) + } + + override val version: NsClient.Version + get() = NsClient.Version.V3 + + override val address: String get() = sp.getString(R.string.key_nsclientinternal_url, "") + + fun handleClearAlarm(originalAlarm: NSAlarm, silenceTimeInMilliseconds: Long) { + if (!isEnabled()) return + if (!sp.getBoolean(R.string.key_ns_upload, true)) { + aapsLogger.debug(LTag.NSCLIENT, "Upload disabled. Message dropped") + return + } + nsClientService?.sendAlarmAck( + AlarmAck().also { ack -> + ack.level = originalAlarm.level() + ack.group = originalAlarm.group() + ack.silenceTime = silenceTimeInMilliseconds + }) + } + + override fun updateLatestBgReceivedIfNewer(latestReceived: Long) { + if (latestReceived > lastFetched.collections.entries) { + lastFetched.collections.entries = latestReceived + storeLastFetched() + } + } + + override fun updateLatestTreatmentReceivedIfNewer(latestReceived: Long) { + lastFetched.collections.treatments = latestReceived + storeLastFetched() + } + + override fun resetToFullSync() { + lastFetched = LastModified( + LastModified.Collections( + dateUtil.now() - maxAge, + dateUtil.now() - maxAge, + dateUtil.now() - maxAge, + dateUtil.now() - maxAge + ) + ) + storeLastFetched() + } + + private fun storeLastFetched() { + sp.putString(R.string.key_nsclientv2_lastmodified, Json.encodeToString(LastModified.serializer(), lastFetched)) + } + + fun test() { + if (workIsRunning(arrayOf(JOB_NAME))) + rxBus.send(EventNSClientNewLog("RUN", "Already running", NsClient.Version.V3)) + else { + rxBus.send(EventNSClientNewLog("RUN", "Starting next round", NsClient.Version.V3)) + WorkManager.getInstance(context) + .beginUniqueWork( + "NSCv3Load", + ExistingWorkPolicy.REPLACE, + OneTimeWorkRequest.Builder(LoadStatusWorker::class.java).build() + ) + .then(OneTimeWorkRequest.Builder(LoadLastModificationWorker::class.java).build()) + .then(OneTimeWorkRequest.Builder(LoadBgWorker::class.java).build()) + // LoadTreatmentsWorker is enqueued after BG finish + //.then(OneTimeWorkRequest.Builder(LoadTreatmentsWorker::class.java).build()) + .enqueue() + } + } + + private fun workIsRunning(workNames: Array): Boolean { + for (workName in workNames) + for (workInfo in WorkManager.getInstance(context).getWorkInfosForUniqueWork(workName).get()) + if (workInfo.state == WorkInfo.State.BLOCKED || workInfo.state == WorkInfo.State.ENQUEUED || workInfo.state == WorkInfo.State.RUNNING) + return true + return false + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/BolusCalculatorResultExtension.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/BolusCalculatorResultExtension.kt new file mode 100644 index 0000000000..ddc6d88c84 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/BolusCalculatorResultExtension.kt @@ -0,0 +1,19 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3.extensions + +import com.google.gson.Gson +import com.google.gson.JsonSyntaxException +import info.nightscout.androidaps.database.entities.BolusCalculatorResult +import info.nightscout.sdk.localmodel.treatment.NSBolusWizard + +fun NSBolusWizard.toBolusCalculatorResult(): BolusCalculatorResult? = + try { + Gson().fromJson(bolusCalculatorResult, BolusCalculatorResult::class.java) + .also { + it.id = 0 + it.isValid = isValid + it.interfaceIDs.nightscoutId = identifier + it.version = 0 + } + } catch (e: JsonSyntaxException) { + null + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/BolusExtension.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/BolusExtension.kt new file mode 100644 index 0000000000..10f41e9343 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/BolusExtension.kt @@ -0,0 +1,24 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3.extensions + +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.entities.Bolus +import info.nightscout.sdk.localmodel.treatment.NSBolus + +fun NSBolus.toBolus(): Bolus = + Bolus( + isValid = isValid, + timestamp = date, + utcOffset = utcOffset, + amount = insulin, + type = type.toBolusType(), + notes = notes, + interfaceIDs_backing = InterfaceIDs(nightscoutId = identifier, pumpId = pumpId, pumpType = InterfaceIDs.PumpType.fromString(pumpType), pumpSerial = pumpSerial, endId = endId) + ) + +fun NSBolus.BolusType?.toBolusType(): Bolus.Type = + when (this) { + NSBolus.BolusType.NORMAL -> Bolus.Type.NORMAL + NSBolus.BolusType.SMB -> Bolus.Type.SMB + NSBolus.BolusType.PRIMING -> Bolus.Type.PRIMING + null -> Bolus.Type.NORMAL + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/CarbsExtension.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/CarbsExtension.kt new file mode 100644 index 0000000000..3c85c8de41 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/CarbsExtension.kt @@ -0,0 +1,16 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3.extensions + +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.entities.Carbs +import info.nightscout.sdk.localmodel.treatment.NSCarbs + +fun NSCarbs.toCarbs(): Carbs = + Carbs( + isValid = isValid, + timestamp = date, + utcOffset = utcOffset, + amount = carbs, + notes = notes, + duration = duration, + interfaceIDs_backing = InterfaceIDs(nightscoutId = identifier, pumpId = pumpId, pumpType = InterfaceIDs.PumpType.fromString(pumpType), pumpSerial = pumpSerial, endId = endId) + ) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/EffectiveProfileSwitchExtension.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/EffectiveProfileSwitchExtension.kt new file mode 100644 index 0000000000..a022d65380 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/EffectiveProfileSwitchExtension.kt @@ -0,0 +1,33 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3.extensions + +import info.nightscout.androidaps.data.ProfileSealed +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson +import info.nightscout.plugins.sync.nsclient.extensions.fromConstant +import info.nightscout.sdk.localmodel.treatment.NSEffectiveProfileSwitch + +fun NSEffectiveProfileSwitch.toEffectiveProfileSwitch(dateUtil: DateUtil): EffectiveProfileSwitch? { + val pureProfile = pureProfileFromJson(profileJson, dateUtil) ?: return null + val profileSealed = ProfileSealed.Pure(pureProfile) + + return EffectiveProfileSwitch( + isValid = isValid, + timestamp = date, + utcOffset = utcOffset, + basalBlocks = profileSealed.basalBlocks, + isfBlocks = profileSealed.isfBlocks, + icBlocks = profileSealed.icBlocks, + targetBlocks = profileSealed.targetBlocks, + glucoseUnit = EffectiveProfileSwitch.GlucoseUnit.fromConstant(profileSealed.units), + originalProfileName = originalProfileName, + originalCustomizedName = originalCustomizedName, + originalTimeshift = originalTimeshift, + originalPercentage = originalPercentage, + originalDuration = originalDuration, + originalEnd = originalEnd, + insulinConfiguration = profileSealed.insulinConfiguration, + interfaceIDs_backing = InterfaceIDs(nightscoutId = identifier, pumpId = pumpId, pumpType = InterfaceIDs.PumpType.fromString(pumpType), pumpSerial = pumpSerial, endId = endId) + ) +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/ExtendedBolusExtension.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/ExtendedBolusExtension.kt new file mode 100644 index 0000000000..41966b4ccb --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/ExtendedBolusExtension.kt @@ -0,0 +1,16 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3.extensions + +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.entities.ExtendedBolus +import info.nightscout.sdk.localmodel.treatment.NSExtendedBolus + +fun NSExtendedBolus.toExtendedBolus(): ExtendedBolus = + ExtendedBolus( + isValid = isValid, + timestamp = date, + utcOffset = utcOffset, + amount = enteredinsulin, + duration = duration, + isEmulatingTempBasal = isEmulatingTempbasal, + interfaceIDs_backing = InterfaceIDs(nightscoutId = identifier, pumpId = pumpId, pumpType = InterfaceIDs.PumpType.fromString(pumpType), pumpSerial = pumpSerial, endId = endId) + ) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/OfflineEventExtension.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/OfflineEventExtension.kt new file mode 100644 index 0000000000..d237881d43 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/OfflineEventExtension.kt @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3.extensions + +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.entities.OfflineEvent +import info.nightscout.sdk.localmodel.treatment.NSOfflineEvent + +fun NSOfflineEvent.toOfflineEvent(): OfflineEvent = + OfflineEvent( + isValid = isValid, + timestamp = date, + utcOffset = utcOffset, + duration = duration, + reason = reason.toReason(), + interfaceIDs_backing = InterfaceIDs(nightscoutId = identifier, pumpId = pumpId, pumpType = InterfaceIDs.PumpType.fromString(pumpType), pumpSerial = pumpSerial, endId = endId) + ) + +fun NSOfflineEvent.Reason?.toReason(): OfflineEvent.Reason = + when (this) { + NSOfflineEvent.Reason.DISCONNECT_PUMP -> OfflineEvent.Reason.DISCONNECT_PUMP + NSOfflineEvent.Reason.SUSPEND -> OfflineEvent.Reason.SUSPEND + NSOfflineEvent.Reason.DISABLE_LOOP -> OfflineEvent.Reason.DISABLE_LOOP + NSOfflineEvent.Reason.SUPER_BOLUS -> OfflineEvent.Reason.SUPER_BOLUS + NSOfflineEvent.Reason.OTHER -> OfflineEvent.Reason.OTHER + null -> OfflineEvent.Reason.OTHER + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/ProfileSwitchExtension.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/ProfileSwitchExtension.kt new file mode 100644 index 0000000000..46652e8598 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/ProfileSwitchExtension.kt @@ -0,0 +1,36 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3.extensions + +import info.nightscout.androidaps.data.ProfileSealed +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.entities.ProfileSwitch +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.extensions.fromConstant +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson +import info.nightscout.sdk.localmodel.treatment.NSProfileSwitch + +fun NSProfileSwitch.toProfileSwitch(activePlugin: ActivePlugin, dateUtil: DateUtil): ProfileSwitch? { + val pureProfile = + profileJson?.let { pureProfileFromJson(it, dateUtil) ?: return null } + ?: activePlugin.activeProfileSource.profile?.getSpecificProfile(profileName) ?: return null + + val profileSealed = ProfileSealed.Pure(pureProfile) + + return ProfileSwitch( + isValid = isValid, + timestamp = date, + utcOffset = utcOffset, + basalBlocks = profileSealed.basalBlocks, + isfBlocks = profileSealed.isfBlocks, + icBlocks = profileSealed.icBlocks, + targetBlocks = profileSealed.targetBlocks, + glucoseUnit = ProfileSwitch.GlucoseUnit.fromConstant(profileSealed.units), + profileName = originalProfileName ?: profileName, + timeshift = timeShift ?: 0, + percentage = percentage ?: 100, + duration = originalDuration ?: T.mins(duration ?: 0).msecs(), + insulinConfiguration = profileSealed.insulinConfiguration, + interfaceIDs_backing = InterfaceIDs(nightscoutId = identifier, pumpId = pumpId, pumpType = InterfaceIDs.PumpType.fromString(pumpType), pumpSerial = pumpSerial, endId = endId) + ) +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/TemporaryBasalExtension.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/TemporaryBasalExtension.kt new file mode 100644 index 0000000000..8735c78443 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/TemporaryBasalExtension.kt @@ -0,0 +1,27 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3.extensions + +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.entities.TemporaryBasal +import info.nightscout.sdk.localmodel.treatment.NSTemporaryBasal + +fun NSTemporaryBasal.toTemporaryBasal(): TemporaryBasal = + TemporaryBasal( + isValid = isValid, + timestamp = date, + utcOffset = utcOffset, + type = type.toType(), + rate = rate, + isAbsolute = isAbsolute, + duration = duration, + interfaceIDs_backing = InterfaceIDs(nightscoutId = identifier, pumpId = pumpId, pumpType = InterfaceIDs.PumpType.fromString(pumpType), pumpSerial = pumpSerial, endId = endId) + ) + +fun NSTemporaryBasal.Type?.toType(): TemporaryBasal.Type = + when (this) { + NSTemporaryBasal.Type.NORMAL -> TemporaryBasal.Type.NORMAL + NSTemporaryBasal.Type.EMULATED_PUMP_SUSPEND -> TemporaryBasal.Type.EMULATED_PUMP_SUSPEND + NSTemporaryBasal.Type.PUMP_SUSPEND -> TemporaryBasal.Type.PUMP_SUSPEND + NSTemporaryBasal.Type.SUPERBOLUS -> TemporaryBasal.Type.SUPERBOLUS + NSTemporaryBasal.Type.FAKE_EXTENDED -> TemporaryBasal.Type.FAKE_EXTENDED + null -> TemporaryBasal.Type.NORMAL + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/TemporaryTargetExtension.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/TemporaryTargetExtension.kt new file mode 100644 index 0000000000..22d1d62038 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/TemporaryTargetExtension.kt @@ -0,0 +1,28 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3.extensions + +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.entities.TemporaryTarget +import info.nightscout.sdk.localmodel.treatment.NSTemporaryTarget + +fun NSTemporaryTarget.toTemporaryTarget(): TemporaryTarget = + TemporaryTarget( + isValid = isValid, + timestamp = date, + utcOffset = utcOffset, + reason = reason.toReason(), + highTarget = targetTop.asMgdl(), + lowTarget = targetBottom.asMgdl(), + duration = duration, + interfaceIDs_backing = InterfaceIDs(nightscoutId = identifier, pumpId = pumpId, pumpType = InterfaceIDs.PumpType.fromString(pumpType), pumpSerial = pumpSerial, endId = endId) + ) + +fun NSTemporaryTarget.Reason?.toReason(): TemporaryTarget.Reason = + when (this) { + NSTemporaryTarget.Reason.CUSTOM -> TemporaryTarget.Reason.CUSTOM + NSTemporaryTarget.Reason.HYPOGLYCEMIA -> TemporaryTarget.Reason.HYPOGLYCEMIA + NSTemporaryTarget.Reason.ACTIVITY -> TemporaryTarget.Reason.ACTIVITY + NSTemporaryTarget.Reason.EATING_SOON -> TemporaryTarget.Reason.EATING_SOON + NSTemporaryTarget.Reason.AUTOMATION -> TemporaryTarget.Reason.AUTOMATION + NSTemporaryTarget.Reason.WEAR -> TemporaryTarget.Reason.WEAR + null -> TemporaryTarget.Reason.CUSTOM + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/TherapyEventExtension.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/TherapyEventExtension.kt new file mode 100644 index 0000000000..4226fe79b8 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/extensions/TherapyEventExtension.kt @@ -0,0 +1,68 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3.extensions + +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.sdk.localmodel.entry.NsUnits +import info.nightscout.sdk.localmodel.treatment.EventType +import info.nightscout.sdk.localmodel.treatment.NSTherapyEvent + +fun NSTherapyEvent.toTherapyEvent(): TherapyEvent = + TherapyEvent( + isValid = isValid, + timestamp = date, + utcOffset = utcOffset, + glucoseUnit = units.toUnits(), + type = eventType.toType(), + note = notes, + enteredBy = enteredBy, + glucose = glucose, + glucoseType = glucoseType.toMeterType(), + duration = duration, + interfaceIDs_backing = InterfaceIDs(nightscoutId = identifier, pumpId = pumpId, pumpType = InterfaceIDs.PumpType.fromString(pumpType), pumpSerial = pumpSerial, endId = endId) + ) + +fun EventType.toType(): TherapyEvent.Type = + when (this) { + EventType.CANNULA_CHANGE -> TherapyEvent.Type.CANNULA_CHANGE + EventType.INSULIN_CHANGE -> TherapyEvent.Type.INSULIN_CHANGE + EventType.PUMP_BATTERY_CHANGE -> TherapyEvent.Type.PUMP_BATTERY_CHANGE + EventType.SENSOR_CHANGE -> TherapyEvent.Type.SENSOR_CHANGE + EventType.SENSOR_STARTED -> TherapyEvent.Type.SENSOR_STARTED + EventType.SENSOR_STOPPED -> TherapyEvent.Type.SENSOR_STOPPED + EventType.FINGER_STICK_BG_VALUE -> TherapyEvent.Type.FINGER_STICK_BG_VALUE + EventType.EXERCISE -> TherapyEvent.Type.EXERCISE + EventType.ANNOUNCEMENT -> TherapyEvent.Type.ANNOUNCEMENT + EventType.QUESTION -> TherapyEvent.Type.QUESTION + EventType.NOTE -> TherapyEvent.Type.NOTE + EventType.APS_OFFLINE -> TherapyEvent.Type.APS_OFFLINE + EventType.DAD_ALERT -> TherapyEvent.Type.DAD_ALERT + EventType.NS_MBG -> TherapyEvent.Type.NS_MBG + EventType.CARBS_CORRECTION -> TherapyEvent.Type.CARBS_CORRECTION + EventType.BOLUS_WIZARD -> TherapyEvent.Type.BOLUS_WIZARD + EventType.CORRECTION_BOLUS -> TherapyEvent.Type.CORRECTION_BOLUS + EventType.MEAL_BOLUS -> TherapyEvent.Type.MEAL_BOLUS + EventType.COMBO_BOLUS -> TherapyEvent.Type.COMBO_BOLUS + EventType.TEMPORARY_TARGET -> TherapyEvent.Type.TEMPORARY_TARGET + EventType.TEMPORARY_TARGET_CANCEL -> TherapyEvent.Type.TEMPORARY_TARGET_CANCEL + EventType.PROFILE_SWITCH -> TherapyEvent.Type.PROFILE_SWITCH + EventType.SNACK_BOLUS -> TherapyEvent.Type.SNACK_BOLUS + EventType.TEMPORARY_BASAL -> TherapyEvent.Type.TEMPORARY_BASAL + EventType.TEMPORARY_BASAL_START -> TherapyEvent.Type.TEMPORARY_BASAL_START + EventType.TEMPORARY_BASAL_END -> TherapyEvent.Type.TEMPORARY_BASAL_END + EventType.NONE -> TherapyEvent.Type.NONE + } + +fun NSTherapyEvent.MeterType?.toMeterType(): TherapyEvent.MeterType = + when (this) { + NSTherapyEvent.MeterType.FINGER -> TherapyEvent.MeterType.FINGER + NSTherapyEvent.MeterType.SENSOR -> TherapyEvent.MeterType.SENSOR + NSTherapyEvent.MeterType.MANUAL -> TherapyEvent.MeterType.MANUAL + null -> TherapyEvent.MeterType.MANUAL + } + +fun NsUnits?.toUnits(): TherapyEvent.GlucoseUnit = + when (this) { + NsUnits.MG_DL -> TherapyEvent.GlucoseUnit.MGDL + NsUnits.MMOL_L -> TherapyEvent.GlucoseUnit.MMOL + null -> TherapyEvent.GlucoseUnit.MGDL + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/LoadBgWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/LoadBgWorker.kt new file mode 100644 index 0000000000..070be71a8a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/LoadBgWorker.kt @@ -0,0 +1,100 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3.workers + +import android.content.Context +import androidx.work.ExistingWorkPolicy +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import androidx.work.Worker +import androidx.work.WorkerParameters +import androidx.work.workDataOf +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.R +import info.nightscout.androidaps.interfaces.NsClient +import info.nightscout.androidaps.plugins.source.NSClientSourcePlugin +import info.nightscout.androidaps.plugins.sync.nsShared.StoreDataForDb +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientNewLog +import info.nightscout.androidaps.plugins.sync.nsclientV3.NSClientV3Plugin +import info.nightscout.androidaps.receivers.DataWorkerStorage +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.rx.bus.RxBus +import info.nightscout.rx.logging.AAPSLogger +import info.nightscout.shared.sharedPreferences.SP +import kotlinx.coroutines.runBlocking +import javax.inject.Inject + +class LoadBgWorker( + context: Context, params: WorkerParameters +) : Worker(context, params) { + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var dataWorkerStorage: DataWorkerStorage + @Inject lateinit var rxBus: RxBus + @Inject lateinit var sp: SP + @Inject lateinit var context: Context + @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var nsClientV3Plugin: NSClientV3Plugin + + companion object { + + val JOB_NAME: String = this::class.java.simpleName + } + + override fun doWork(): Result { + var ret = Result.success() + + runBlocking { + if ((nsClientV3Plugin.lastModified?.collections?.entries ?: Long.MAX_VALUE) > nsClientV3Plugin.lastFetched.collections.entries) + try { + //val sgvs = nsClientV3Plugin.nsAndroidClient.getSgvsModifiedSince(nsClientV3Plugin.lastFetched.collections.entries) + val sgvs = nsClientV3Plugin.nsAndroidClient.getSgvsNewerThan(nsClientV3Plugin.lastFetched.collections.entries, 500) + aapsLogger.debug("SGVS: $sgvs") + if (sgvs.isNotEmpty()) { + rxBus.send( + EventNSClientNewLog( + "RCV", + "${sgvs.size} SVGs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastFetched.collections.entries)}", + NsClient.Version.V3 + ) + ) + // Objective0 + sp.putBoolean(R.string.key_ObjectivesbgIsAvailableInNS, true) + // Schedule processing of fetched data and continue of loading + WorkManager.getInstance(context).beginUniqueWork( + JOB_NAME, + ExistingWorkPolicy.APPEND_OR_REPLACE, + OneTimeWorkRequest.Builder(NSClientSourcePlugin.NSClientSourceWorker::class.java).setInputData(dataWorkerStorage.storeInputData(sgvs)).build() + ).then(OneTimeWorkRequest.Builder(LoadBgWorker::class.java).build()).enqueue() + } else { + rxBus.send(EventNSClientNewLog("END", "No SGVs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastFetched.collections.entries)}", NsClient.Version.V3)) + WorkManager.getInstance(context) + .beginUniqueWork( + NSClientV3Plugin.JOB_NAME, + ExistingWorkPolicy.APPEND_OR_REPLACE, + OneTimeWorkRequest.Builder(StoreDataForDb.StoreBgWorker::class.java).build() + ) + .then(OneTimeWorkRequest.Builder(LoadTreatmentsWorker::class.java).build()) + .enqueue() + } + } catch (error: Exception) { + aapsLogger.error("Error: ", error) + ret = Result.failure(workDataOf("Error" to error.toString())) + } + else { + rxBus.send(EventNSClientNewLog("END", "No new SGVs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastFetched.collections.entries)}", NsClient.Version.V3)) + WorkManager.getInstance(context) + .beginUniqueWork( + NSClientV3Plugin.JOB_NAME, + ExistingWorkPolicy.APPEND_OR_REPLACE, + OneTimeWorkRequest.Builder(StoreDataForDb.StoreBgWorker::class.java).build() + ) + .then(OneTimeWorkRequest.Builder(LoadTreatmentsWorker::class.java).build()) + .enqueue() + } + } + return ret + } + + init { + (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/LoadDeviceStatusWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/LoadDeviceStatusWorker.kt new file mode 100644 index 0000000000..2f07cb139c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/LoadDeviceStatusWorker.kt @@ -0,0 +1,58 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3.workers + +import android.content.Context +import androidx.work.Worker +import androidx.work.WorkerParameters +import androidx.work.workDataOf +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.interfaces.NsClient +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientNewLog +import info.nightscout.androidaps.plugins.sync.nsclient.data.NSDeviceStatusHandler +import info.nightscout.androidaps.plugins.sync.nsclientV3.NSClientV3Plugin +import info.nightscout.androidaps.receivers.DataWorkerStorage +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.T +import info.nightscout.rx.bus.RxBus +import info.nightscout.rx.logging.AAPSLogger +import kotlinx.coroutines.runBlocking +import javax.inject.Inject + +class LoadDeviceStatusWorker( + context: Context, + params: WorkerParameters +) : Worker(context, params) { + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var dataWorkerStorage: DataWorkerStorage + @Inject lateinit var rxBus: RxBus + @Inject lateinit var context: Context + @Inject lateinit var nsClientV3Plugin: NSClientV3Plugin + @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var nsDeviceStatusHandler: NSDeviceStatusHandler + + override fun doWork(): Result { + var ret = Result.success() + + runBlocking { + try { + val from = dateUtil.now() - T.mins(7).msecs() + val deviceStatuses = nsClientV3Plugin.nsAndroidClient.getDeviceStatusModifiedSince(from) + aapsLogger.debug("DEVICESTATUSES: $deviceStatuses") + if (deviceStatuses.isNotEmpty()) { + rxBus.send(EventNSClientNewLog("RCV", "${deviceStatuses.size} DSs from ${dateUtil.dateAndTimeAndSecondsString(from)}", NsClient.Version.V3)) + nsDeviceStatusHandler.handleNewData(deviceStatuses.toTypedArray(), NsClient.Version.V3) + } else { + rxBus.send(EventNSClientNewLog("END", "No DSs from ${dateUtil.dateAndTimeAndSecondsString(from)}", NsClient.Version.V3)) + } + } catch (error: Exception) { + aapsLogger.error("Error: ", error) + ret = Result.failure(workDataOf("Error" to error.toString())) + } + } + return ret + } + + init { + (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/LoadLastModificationWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/LoadLastModificationWorker.kt new file mode 100644 index 0000000000..62162e7367 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/LoadLastModificationWorker.kt @@ -0,0 +1,39 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3.workers + +import android.content.Context +import androidx.work.Worker +import androidx.work.WorkerParameters +import androidx.work.workDataOf +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.plugins.sync.nsclientV3.NSClientV3Plugin +import info.nightscout.rx.logging.AAPSLogger +import kotlinx.coroutines.runBlocking +import javax.inject.Inject + +class LoadLastModificationWorker( + context: Context, params: WorkerParameters +) : Worker(context, params) { + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var nsClientV3Plugin: NSClientV3Plugin + + override fun doWork(): Result { + var ret = Result.success() + + runBlocking { + try { + val lm = nsClientV3Plugin.nsAndroidClient.getLastModified() + nsClientV3Plugin.lastModified = lm + aapsLogger.debug("LAST MODIFIED: ${nsClientV3Plugin.lastModified}") + } catch (error: Exception) { + aapsLogger.error("Error: ", error) + ret = Result.failure(workDataOf("Error" to error.toString())) + } + } + return ret + } + + init { + (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/LoadStatusWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/LoadStatusWorker.kt new file mode 100644 index 0000000000..bdebb19219 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/LoadStatusWorker.kt @@ -0,0 +1,38 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3.workers + +import android.content.Context +import androidx.work.Worker +import androidx.work.WorkerParameters +import androidx.work.workDataOf +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.plugins.sync.nsclientV3.NSClientV3Plugin +import info.nightscout.rx.logging.AAPSLogger +import kotlinx.coroutines.runBlocking +import javax.inject.Inject + +class LoadStatusWorker( + context: Context, params: WorkerParameters +) : Worker(context, params) { + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var nsClientV3Plugin: NSClientV3Plugin + + override fun doWork(): Result { + var ret = Result.success() + + runBlocking { + try { + val status = nsClientV3Plugin.nsAndroidClient.getStatus() + aapsLogger.debug("STATUS: $status") + } catch (error: Exception) { + aapsLogger.error("Error: ", error) + ret = Result.failure(workDataOf("Error" to error.toString())) + } + } + return ret + } + + init { + (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/LoadTreatmentsWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/LoadTreatmentsWorker.kt new file mode 100644 index 0000000000..e7b98e3355 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/LoadTreatmentsWorker.kt @@ -0,0 +1,97 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3.workers + +import android.content.Context +import androidx.work.ExistingWorkPolicy +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import androidx.work.Worker +import androidx.work.WorkerParameters +import androidx.work.workDataOf +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.interfaces.NsClient +import info.nightscout.androidaps.plugins.sync.nsShared.StoreDataForDb +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientNewLog +import info.nightscout.androidaps.plugins.sync.nsclientV3.NSClientV3Plugin +import info.nightscout.androidaps.receivers.DataWorkerStorage +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.rx.bus.RxBus +import info.nightscout.rx.logging.AAPSLogger +import kotlinx.coroutines.runBlocking +import javax.inject.Inject + +class LoadTreatmentsWorker( + context: Context, + params: WorkerParameters +) : Worker(context, params) { + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var dataWorkerStorage: DataWorkerStorage + @Inject lateinit var rxBus: RxBus + @Inject lateinit var context: Context + @Inject lateinit var nsClientV3Plugin: NSClientV3Plugin + @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var storeDataForDb: StoreDataForDb + + override fun doWork(): Result { + var ret = Result.success() + + runBlocking { + if ((nsClientV3Plugin.lastModified?.collections?.treatments ?: Long.MAX_VALUE) > nsClientV3Plugin.lastFetched.collections.treatments) + try { + val treatments = nsClientV3Plugin.nsAndroidClient.getTreatmentsModifiedSince(nsClientV3Plugin.lastFetched.collections.treatments, 500) + aapsLogger.debug("TREATMENTS: $treatments") + if (treatments.isNotEmpty()) { + rxBus.send( + EventNSClientNewLog( + "RCV", + "${treatments.size} TRs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastFetched.collections.treatments)}", + NsClient.Version.V3 + ) + ) + // Schedule processing of fetched data and continue of loading + WorkManager.getInstance(context) + .beginUniqueWork( + NSClientV3Plugin.JOB_NAME, + ExistingWorkPolicy.APPEND_OR_REPLACE, + OneTimeWorkRequest.Builder(ProcessTreatmentsWorker::class.java) + .setInputData(dataWorkerStorage.storeInputData(treatments)) + .build() + ).then(OneTimeWorkRequest.Builder(LoadTreatmentsWorker::class.java).build()) + .enqueue() + } else { + rxBus.send( + EventNSClientNewLog( + "END", "No TRs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastFetched.collections.treatments)}", + NsClient.Version.V3 + ) + ) + storeDataForDb.storeTreatmentsToDb() + WorkManager.getInstance(context) + .enqueueUniqueWork( + NSClientV3Plugin.JOB_NAME, + ExistingWorkPolicy.APPEND_OR_REPLACE, + OneTimeWorkRequest.Builder(LoadDeviceStatusWorker::class.java).build() + ) + } + } catch (error: Exception) { + aapsLogger.error("Error: ", error) + ret = Result.failure(workDataOf("Error" to error.toString())) + } + else { + rxBus.send(EventNSClientNewLog("END", "No new TRs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastFetched.collections.treatments)}", NsClient.Version.V3)) + storeDataForDb.storeTreatmentsToDb() + WorkManager.getInstance(context) + .enqueueUniqueWork( + NSClientV3Plugin.JOB_NAME, + ExistingWorkPolicy.APPEND_OR_REPLACE, + OneTimeWorkRequest.Builder(LoadDeviceStatusWorker::class.java).build() + ) + } + } + return ret + } + + init { + (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/ProcessTreatmentsWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/ProcessTreatmentsWorker.kt new file mode 100644 index 0000000000..e1c48c4eb7 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/nsclientV3/workers/ProcessTreatmentsWorker.kt @@ -0,0 +1,158 @@ +package info.nightscout.androidaps.plugins.sync.nsclientV3.workers + +import android.content.Context +import androidx.work.Worker +import androidx.work.WorkerParameters +import androidx.work.workDataOf +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.R +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.XDripBroadcast +import info.nightscout.androidaps.logging.UserEntryLogger +import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin +import info.nightscout.androidaps.plugins.sync.nsShared.StoreDataForDb +import info.nightscout.androidaps.plugins.sync.nsclientV3.extensions.toBolus +import info.nightscout.androidaps.plugins.sync.nsclientV3.extensions.toBolusCalculatorResult +import info.nightscout.androidaps.plugins.sync.nsclientV3.extensions.toCarbs +import info.nightscout.androidaps.plugins.sync.nsclientV3.extensions.toEffectiveProfileSwitch +import info.nightscout.androidaps.plugins.sync.nsclientV3.extensions.toExtendedBolus +import info.nightscout.androidaps.plugins.sync.nsclientV3.extensions.toOfflineEvent +import info.nightscout.androidaps.plugins.sync.nsclientV3.extensions.toProfileSwitch +import info.nightscout.androidaps.plugins.sync.nsclientV3.extensions.toTemporaryBasal +import info.nightscout.androidaps.plugins.sync.nsclientV3.extensions.toTemporaryTarget +import info.nightscout.androidaps.plugins.sync.nsclientV3.extensions.toTherapyEvent +import info.nightscout.androidaps.receivers.DataWorkerStorage +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.interfaces.BuildHelper +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.Constants +import info.nightscout.rx.bus.RxBus +import info.nightscout.rx.logging.AAPSLogger +import info.nightscout.rx.logging.LTag +import info.nightscout.sdk.localmodel.treatment.NSBolus +import info.nightscout.sdk.localmodel.treatment.NSBolusWizard +import info.nightscout.sdk.localmodel.treatment.NSCarbs +import info.nightscout.sdk.localmodel.treatment.NSEffectiveProfileSwitch +import info.nightscout.sdk.localmodel.treatment.NSExtendedBolus +import info.nightscout.sdk.localmodel.treatment.NSOfflineEvent +import info.nightscout.sdk.localmodel.treatment.NSProfileSwitch +import info.nightscout.sdk.localmodel.treatment.NSTemporaryBasal +import info.nightscout.sdk.localmodel.treatment.NSTemporaryTarget +import info.nightscout.sdk.localmodel.treatment.NSTherapyEvent +import info.nightscout.sdk.localmodel.treatment.NSTreatment +import info.nightscout.shared.sharedPreferences.SP +import javax.inject.Inject + +class ProcessTreatmentsWorker( + context: Context, + params: WorkerParameters +) : Worker(context, params) { + + @Inject lateinit var dataWorkerStorage: DataWorkerStorage + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var buildHelper: BuildHelper + @Inject lateinit var sp: SP + @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var config: Config + @Inject lateinit var repository: AppRepository + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var rxBus: RxBus + @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var virtualPumpPlugin: VirtualPumpPlugin + @Inject lateinit var xDripBroadcast: XDripBroadcast + @Inject lateinit var storeDataForDb: StoreDataForDb + + override fun doWork(): Result { + @Suppress("UNCHECKED_CAST") + val treatments = dataWorkerStorage.pickupObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) as List? + ?: return Result.failure(workDataOf("Error" to "missing input data")) + + val ret = Result.success() + var latestDateInReceivedData = 0L + + for (treatment in treatments) { + aapsLogger.debug(LTag.DATABASE, "Received NS treatment: $treatment") + + //Find latest date in treatment + val mills = treatment.date + if (mills != 0L && mills < dateUtil.now()) + if (mills > latestDateInReceivedData) latestDateInReceivedData = mills + + when (treatment) { + is NSBolus -> + if (sp.getBoolean(R.string.key_ns_receive_insulin, false) || config.NSCLIENT) + storeDataForDb.boluses.add(treatment.toBolus()) + + is NSCarbs -> + if (sp.getBoolean(R.string.key_ns_receive_carbs, false) || config.NSCLIENT) + storeDataForDb.carbs.add(treatment.toCarbs()) + + is NSTemporaryTarget -> + if (sp.getBoolean(R.string.key_ns_receive_temp_target, false) || config.NSCLIENT) { + if (treatment.duration > 0L) { + // not ending event + if (treatment.targetBottomAsMgdl() < Constants.MIN_TT_MGDL + || treatment.targetBottomAsMgdl() > Constants.MAX_TT_MGDL + || treatment.targetTopAsMgdl() < Constants.MIN_TT_MGDL + || treatment.targetTopAsMgdl() > Constants.MAX_TT_MGDL + || treatment.targetBottomAsMgdl() > treatment.targetTopAsMgdl() + ) { + aapsLogger.debug(LTag.DATABASE, "Ignored TemporaryTarget $treatment") + continue + } + } + storeDataForDb.temporaryTargets.add(treatment.toTemporaryTarget()) + } + + is NSTemporaryBasal -> + if (buildHelper.isEngineeringMode() && sp.getBoolean(R.string.key_ns_receive_tbr_eb, false) || config.NSCLIENT) + storeDataForDb.temporaryBasals.add(treatment.toTemporaryBasal()) + + is NSEffectiveProfileSwitch -> + if (sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || config.NSCLIENT) { + treatment.toEffectiveProfileSwitch(dateUtil)?.let { effectiveProfileSwitch -> + storeDataForDb.effectiveProfileSwitches.add(effectiveProfileSwitch) + } + } + + is NSProfileSwitch -> + if (sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || config.NSCLIENT) { + treatment.toProfileSwitch(activePlugin, dateUtil)?.let { profileSwitch -> + storeDataForDb.profileSwitches.add(profileSwitch) + } + } + + is NSBolusWizard -> + treatment.toBolusCalculatorResult()?.let { bolusCalculatorResult -> + storeDataForDb.bolusCalculatorResults.add(bolusCalculatorResult) + } + + is NSTherapyEvent -> + if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) + treatment.toTherapyEvent().let { therapyEvent -> + storeDataForDb.therapyEvents.add(therapyEvent) + } + + is NSOfflineEvent -> + if (sp.getBoolean(R.string.key_ns_receive_offline_event, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) + treatment.toOfflineEvent().let { offlineEvent -> + storeDataForDb.offlineEvents.add(offlineEvent) + } + + is NSExtendedBolus -> + if (buildHelper.isEngineeringMode() && sp.getBoolean(R.string.key_ns_receive_tbr_eb, false) || config.NSCLIENT) + treatment.toExtendedBolus().let { extendedBolus -> + storeDataForDb.extendedBoluses.add(extendedBolus) + } + } + } + activePlugin.activeNsClient?.updateLatestTreatmentReceivedIfNewer(latestDateInReceivedData) +// xDripBroadcast.sendTreatments(treatments) + return ret + } + + init { + (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/TidepoolFragment.kt similarity index 78% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolFragment.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/TidepoolFragment.kt index a757b6e6a8..4d13020853 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/TidepoolFragment.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool +package info.nightscout.androidaps.plugins.sync.tidepool import android.os.Bundle import android.view.LayoutInflater @@ -8,10 +8,10 @@ import android.widget.ScrollView import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R import info.nightscout.androidaps.databinding.TidepoolFragmentBinding -import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolDoUpload import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolResetData -import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolUpdateGUI +import info.nightscout.androidaps.plugins.sync.tidepool.comm.TidepoolUploader +import info.nightscout.androidaps.plugins.sync.tidepool.events.EventTidepoolUpdateGUI import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus @@ -57,13 +57,13 @@ class TidepoolFragment : DaggerFragment() { .toObservable(EventTidepoolUpdateGUI::class.java) .observeOn(aapsSchedulers.main) .subscribe({ - if (_binding == null) return@subscribe - tidepoolPlugin.updateLog() - binding.log.text = tidepoolPlugin.textLog - binding.status.text = tidepoolUploader.connectionStatus.name - binding.log.text = tidepoolPlugin.textLog - binding.logscrollview.fullScroll(ScrollView.FOCUS_DOWN) - }, fabricPrivacy::logException) + if (_binding == null) return@subscribe + tidepoolPlugin.updateLog() + binding.log.text = tidepoolPlugin.textLog + binding.status.text = tidepoolUploader.connectionStatus.name + binding.log.text = tidepoolPlugin.textLog + binding.logscrollview.fullScroll(ScrollView.FOCUS_DOWN) + }, fabricPrivacy::logException) } @Synchronized diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/TidepoolPlugin.kt similarity index 62% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/TidepoolPlugin.kt index 5af4f7a711..74b342cb94 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/TidepoolPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/TidepoolPlugin.kt @@ -1,32 +1,31 @@ -package info.nightscout.androidaps.plugins.general.tidepool +package info.nightscout.androidaps.plugins.sync.tidepool import android.content.Context import android.text.Spanned import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import dagger.android.HasAndroidInjector -import info.nightscout.interfaces.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.events.EventNewBG import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.interfaces.PluginBase -import info.nightscout.interfaces.PluginDescription -import info.nightscout.interfaces.PluginType import info.nightscout.androidaps.interfaces.ResourceHelper -import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader -import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader.ConnectionStatus.CONNECTED -import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader.ConnectionStatus.DISCONNECTED -import info.nightscout.androidaps.plugins.general.tidepool.comm.UploadChunk +import info.nightscout.androidaps.interfaces.Sync import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolDoUpload import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolResetData -import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolStatus -import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolUpdateGUI -import info.nightscout.androidaps.plugins.general.tidepool.utils.RateLimit +import info.nightscout.androidaps.plugins.sync.tidepool.comm.TidepoolUploader +import info.nightscout.androidaps.plugins.sync.tidepool.comm.UploadChunk +import info.nightscout.androidaps.plugins.sync.tidepool.events.EventTidepoolStatus +import info.nightscout.androidaps.plugins.sync.tidepool.events.EventTidepoolUpdateGUI +import info.nightscout.androidaps.plugins.sync.tidepool.utils.RateLimit import info.nightscout.androidaps.receivers.ReceiverStatusStore import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.interfaces.utils.HtmlHelper import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.ToastUtils +import info.nightscout.interfaces.Constants +import info.nightscout.interfaces.PluginDescription +import info.nightscout.interfaces.PluginType +import info.nightscout.interfaces.utils.HtmlHelper import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventNetworkChange @@ -52,14 +51,14 @@ class TidepoolPlugin @Inject constructor( private val sp: SP, private val rateLimit: RateLimit, private val receiverStatusStore: ReceiverStatusStore -) : PluginBase( +) : Sync, PluginBase( PluginDescription() - .mainType(PluginType.GENERAL) - .pluginName(R.string.tidepool) - .shortName(R.string.tidepool_shortname) - .fragmentClass(TidepoolFragment::class.qualifiedName) - .preferencesId(R.xml.pref_tidepool) - .description(R.string.description_tidepool), + .mainType(PluginType.SYNC) + .pluginName(R.string.tidepool) + .shortName(R.string.tidepool_shortname) + .fragmentClass(TidepoolFragment::class.qualifiedName) + .preferencesId(R.xml.pref_tidepool) + .description(R.string.description_tidepool), aapsLogger, rh, injector ) { @@ -79,14 +78,14 @@ class TidepoolPlugin @Inject constructor( .toObservable(EventTidepoolResetData::class.java) .observeOn(aapsSchedulers.io) .subscribe({ - if (tidepoolUploader.connectionStatus != CONNECTED) { - aapsLogger.debug(LTag.TIDEPOOL, "Not connected for delete Dataset") - } else { - tidepoolUploader.deleteDataSet() - sp.putLong(R.string.key_tidepool_last_end, 0) - tidepoolUploader.doLogin() - } - }, fabricPrivacy::logException) + if (tidepoolUploader.connectionStatus != TidepoolUploader.ConnectionStatus.CONNECTED) { + aapsLogger.debug(LTag.TIDEPOOL, "Not connected for delete Dataset") + } else { + tidepoolUploader.deleteDataSet() + sp.putLong(R.string.key_tidepool_last_end, 0) + tidepoolUploader.doLogin() + } + }, fabricPrivacy::logException) disposable += rxBus .toObservable(EventTidepoolStatus::class.java) .observeOn(aapsSchedulers.io) @@ -95,26 +94,27 @@ class TidepoolPlugin @Inject constructor( .toObservable(EventNewBG::class.java) .observeOn(aapsSchedulers.io) .filter { it.glucoseValue != null } // better would be optional in API level >24 - .map { it.glucoseValue } + .map { it.glucoseValue!! } .subscribe({ bgReading -> - if (bgReading!!.timestamp < uploadChunk.getLastEnd()) - uploadChunk.setLastEnd(bgReading.timestamp ) - if (isEnabled(PluginType.GENERAL) - && (!sp.getBoolean(R.string.key_tidepool_only_while_charging, false) || receiverStatusStore.isCharging) - && (!sp.getBoolean(R.string.key_tidepool_only_while_unmetered, false) || receiverStatusStore.isWifiConnected) - && rateLimit.rateLimit("tidepool-new-data-upload", T.mins(4).secs().toInt())) - doUpload() - }, fabricPrivacy::logException) + if (bgReading!!.timestamp < uploadChunk.getLastEnd()) + uploadChunk.setLastEnd(bgReading.timestamp) + if (isEnabled() + && (!sp.getBoolean(R.string.key_tidepool_only_while_charging, false) || receiverStatusStore.isCharging) + && (!sp.getBoolean(R.string.key_tidepool_only_while_unmetered, false) || receiverStatusStore.isWifiConnected) + && rateLimit.rateLimit("tidepool-new-data-upload", T.mins(4).secs().toInt()) + ) + doUpload() + }, fabricPrivacy::logException) disposable += rxBus .toObservable(EventPreferenceChange::class.java) .observeOn(aapsSchedulers.io) .subscribe({ event -> - if (event.isChanged(rh, R.string.key_tidepool_dev_servers) - || event.isChanged(rh, R.string.key_tidepool_username) - || event.isChanged(rh, R.string.key_tidepool_password) - ) - tidepoolUploader.resetInstance() - }, fabricPrivacy::logException) + if (event.isChanged(rh, R.string.key_tidepool_dev_servers) + || event.isChanged(rh, R.string.key_tidepool_username) + || event.isChanged(rh, R.string.key_tidepool_password) + ) + tidepoolUploader.resetInstance() + }, fabricPrivacy::logException) disposable += rxBus .toObservable(EventNetworkChange::class.java) .observeOn(aapsSchedulers.io) @@ -141,10 +141,10 @@ class TidepoolPlugin @Inject constructor( private fun doUpload() = when (tidepoolUploader.connectionStatus) { - DISCONNECTED -> tidepoolUploader.doLogin(true) - CONNECTED -> tidepoolUploader.doUpload() + TidepoolUploader.ConnectionStatus.DISCONNECTED -> tidepoolUploader.doLogin(true) + TidepoolUploader.ConnectionStatus.CONNECTED -> tidepoolUploader.doUpload() - else -> { + else -> { } } @@ -175,4 +175,10 @@ class TidepoolPlugin @Inject constructor( } } + override val status: String + get() = tidepoolUploader.connectionStatus.name + override val hasWritePermission: Boolean + get() = tidepoolUploader.connectionStatus == TidepoolUploader.ConnectionStatus.CONNECTED + override val connected: Boolean + get() = tidepoolUploader.connectionStatus == TidepoolUploader.ConnectionStatus.CONNECTED } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/InfoInterceptor.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/InfoInterceptor.kt similarity index 92% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/InfoInterceptor.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/InfoInterceptor.kt index 748d427d43..0f29e5233f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/InfoInterceptor.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/InfoInterceptor.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.comm +package info.nightscout.androidaps.plugins.sync.tidepool.comm import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/Session.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/Session.kt similarity index 81% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/Session.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/Session.kt index 5fb6b34605..6b3622851f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/Session.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/Session.kt @@ -1,7 +1,7 @@ -package info.nightscout.androidaps.plugins.general.tidepool.comm +package info.nightscout.androidaps.plugins.sync.tidepool.comm -import info.nightscout.androidaps.plugins.general.tidepool.messages.AuthReplyMessage -import info.nightscout.androidaps.plugins.general.tidepool.messages.DatasetReplyMessage +import info.nightscout.androidaps.plugins.sync.tidepool.messages.AuthReplyMessage +import info.nightscout.androidaps.plugins.sync.tidepool.messages.DatasetReplyMessage import okhttp3.Headers class Session(val authHeader: String?, diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolApiService.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/TidepoolApiService.kt similarity index 85% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolApiService.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/TidepoolApiService.kt index 52adf539c5..f9c0491e81 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolApiService.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/TidepoolApiService.kt @@ -1,9 +1,9 @@ -package info.nightscout.androidaps.plugins.general.tidepool.comm +package info.nightscout.androidaps.plugins.sync.tidepool.comm import info.nightscout.androidaps.BuildConfig -import info.nightscout.androidaps.plugins.general.tidepool.messages.AuthReplyMessage -import info.nightscout.androidaps.plugins.general.tidepool.messages.DatasetReplyMessage -import info.nightscout.androidaps.plugins.general.tidepool.messages.UploadReplyMessage +import info.nightscout.androidaps.plugins.sync.tidepool.messages.AuthReplyMessage +import info.nightscout.androidaps.plugins.sync.tidepool.messages.DatasetReplyMessage +import info.nightscout.androidaps.plugins.sync.tidepool.messages.UploadReplyMessage import okhttp3.RequestBody import retrofit2.Call import retrofit2.http.* diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolCallback.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/TidepoolCallback.kt similarity index 87% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolCallback.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/TidepoolCallback.kt index 909c84291a..0e7334378b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolCallback.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/TidepoolCallback.kt @@ -1,6 +1,6 @@ -package info.nightscout.androidaps.plugins.general.tidepool.comm +package info.nightscout.androidaps.plugins.sync.tidepool.comm -import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolStatus +import info.nightscout.androidaps.plugins.sync.tidepool.events.EventTidepoolStatus import info.nightscout.rx.bus.RxBus import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag @@ -8,7 +8,8 @@ import retrofit2.Call import retrofit2.Callback import retrofit2.Response -internal class TidepoolCallback(private val aapsLogger: AAPSLogger, private val rxBus: RxBus, private val session: Session, val name: String, val onSuccess: () -> Unit, val onFail: () -> Unit) : Callback { +internal class TidepoolCallback(private val aapsLogger: AAPSLogger, private val rxBus: RxBus, private val session: Session, val name: String, val onSuccess: () -> Unit, val onFail: () -> Unit) : + Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful && response.body() != null) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolUploader.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/TidepoolUploader.kt similarity index 77% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolUploader.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/TidepoolUploader.kt index 285dd766c9..54472b090a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolUploader.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/TidepoolUploader.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.comm +package info.nightscout.androidaps.plugins.sync.tidepool.comm import android.content.Context import android.os.PowerManager @@ -7,12 +7,12 @@ import info.nightscout.androidaps.BuildConfig import info.nightscout.androidaps.R import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ResourceHelper -import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolStatus -import info.nightscout.androidaps.plugins.general.tidepool.messages.AuthReplyMessage -import info.nightscout.androidaps.plugins.general.tidepool.messages.AuthRequestMessage -import info.nightscout.androidaps.plugins.general.tidepool.messages.DatasetReplyMessage -import info.nightscout.androidaps.plugins.general.tidepool.messages.OpenDatasetRequestMessage -import info.nightscout.androidaps.plugins.general.tidepool.messages.UploadReplyMessage +import info.nightscout.androidaps.plugins.sync.tidepool.events.EventTidepoolStatus +import info.nightscout.androidaps.plugins.sync.tidepool.messages.AuthReplyMessage +import info.nightscout.androidaps.plugins.sync.tidepool.messages.AuthRequestMessage +import info.nightscout.androidaps.plugins.sync.tidepool.messages.DatasetReplyMessage +import info.nightscout.androidaps.plugins.sync.tidepool.messages.OpenDatasetRequestMessage +import info.nightscout.androidaps.plugins.sync.tidepool.messages.UploadReplyMessage import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.alertDialogs.OKDialog @@ -111,9 +111,9 @@ class TidepoolUploader @Inject constructor( call?.enqueue(TidepoolCallback(aapsLogger, rxBus, session!!, "Login", { startSession(session!!, doUpload) }, { - connectionStatus = ConnectionStatus.FAILED - releaseWakeLock() - })) + connectionStatus = ConnectionStatus.FAILED + releaseWakeLock() + })) return } else { aapsLogger.debug(LTag.TIDEPOOL, "Cannot do login as user credentials have not been set correctly") @@ -132,8 +132,12 @@ class TidepoolUploader @Inject constructor( call?.enqueue(TidepoolCallback(aapsLogger, rxBus, session, "Login", { OKDialog.show(rootContext, rh.gs(R.string.tidepool), "Successfully logged into Tidepool.") }, { - OKDialog.show(rootContext, rh.gs(R.string.tidepool), "Failed to log into Tidepool.\nCheck that your user name and password are correct.") - })) + OKDialog.show( + rootContext, + rh.gs(R.string.tidepool), + "Failed to log into Tidepool.\nCheck that your user name and password are correct." + ) + })) } ?: OKDialog.show(rootContext, rh.gs(R.string.tidepool), "Cannot do login as user credentials have not been set correctly") @@ -144,14 +148,18 @@ class TidepoolUploader @Inject constructor( extendWakeLock(30000) if (session.authReply?.userid != null) { // See if we already have an open data set to write to - val datasetCall = session.service!!.getOpenDataSets(session.token!!, - session.authReply!!.userid!!, BuildConfig.APPLICATION_ID, 1) + val datasetCall = session.service!!.getOpenDataSets( + session.token!!, + session.authReply!!.userid!!, BuildConfig.APPLICATION_ID, 1 + ) datasetCall.enqueue(TidepoolCallback>(aapsLogger, rxBus, session, "Get Open Datasets", { if (session.datasetReply == null) { rxBus.send(EventTidepoolStatus(("Creating new dataset"))) - val call = session.service.openDataSet(session.token!!, session.authReply!!.userid!!, - OpenDatasetRequestMessage(activePlugin.activePump.serialNumber(), dateUtil).getBody()) + val call = session.service.openDataSet( + session.token!!, session.authReply!!.userid!!, + OpenDatasetRequestMessage(activePlugin.activePump.serialNumber(), dateUtil).getBody() + ) call.enqueue(TidepoolCallback(aapsLogger, rxBus, session, "Open New Dataset", { connectionStatus = ConnectionStatus.CONNECTED rxBus.send(EventTidepoolStatus(("New dataset OK"))) @@ -159,10 +167,10 @@ class TidepoolUploader @Inject constructor( else releaseWakeLock() }, { - rxBus.send(EventTidepoolStatus(("New dataset FAILED"))) - connectionStatus = ConnectionStatus.FAILED - releaseWakeLock() - })) + rxBus.send(EventTidepoolStatus(("New dataset FAILED"))) + connectionStatus = ConnectionStatus.FAILED + releaseWakeLock() + })) } else { aapsLogger.debug(LTag.TIDEPOOL, "Existing Dataset: " + session.datasetReply!!.getUploadId()) // TODO: Wouldn't need to do this if we could block on the above `call.enqueue`. @@ -174,10 +182,10 @@ class TidepoolUploader @Inject constructor( releaseWakeLock() } }, { - connectionStatus = ConnectionStatus.FAILED - rxBus.send(EventTidepoolStatus(("Open dataset FAILED"))) - releaseWakeLock() - })) + connectionStatus = ConnectionStatus.FAILED + rxBus.send(EventTidepoolStatus(("Open dataset FAILED"))) + releaseWakeLock() + })) } else { aapsLogger.error("Got login response but cannot determine userId - cannot proceed") connectionStatus = ConnectionStatus.FAILED @@ -222,9 +230,9 @@ class TidepoolUploader @Inject constructor( releaseWakeLock() uploadNext() }, { - rxBus.send(EventTidepoolStatus(("Upload FAILED"))) - releaseWakeLock() - })) + rxBus.send(EventTidepoolStatus(("Upload FAILED"))) + releaseWakeLock() + })) } } } @@ -248,10 +256,10 @@ class TidepoolUploader @Inject constructor( rxBus.send(EventTidepoolStatus(("Dataset removed OK"))) releaseWakeLock() }, { - connectionStatus = ConnectionStatus.DISCONNECTED - rxBus.send(EventTidepoolStatus(("Dataset remove FAILED"))) - releaseWakeLock() - })) + connectionStatus = ConnectionStatus.DISCONNECTED + rxBus.send(EventTidepoolStatus(("Dataset remove FAILED"))) + releaseWakeLock() + })) } else { aapsLogger.error("Got login response but cannot determine datasetId - cannot proceed") } @@ -273,10 +281,10 @@ class TidepoolUploader @Inject constructor( rxBus.send(EventTidepoolStatus(("All data removed OK"))) releaseWakeLock() }, { - connectionStatus = ConnectionStatus.DISCONNECTED - rxBus.send(EventTidepoolStatus(("All data remove FAILED"))) - releaseWakeLock() - })) + connectionStatus = ConnectionStatus.DISCONNECTED + rxBus.send(EventTidepoolStatus(("All data remove FAILED"))) + releaseWakeLock() + })) } catch (e: IllegalArgumentException) { aapsLogger.error("Got login response but cannot determine userId - cannot proceed") } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/UploadChunk.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/UploadChunk.kt similarity index 89% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/UploadChunk.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/UploadChunk.kt index 532ef1f095..2085630b64 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/UploadChunk.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/UploadChunk.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.comm +package info.nightscout.androidaps.plugins.sync.tidepool.comm import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository @@ -6,15 +6,15 @@ import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch import info.nightscout.androidaps.database.entities.TemporaryBasal import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.plugins.general.tidepool.elements.BasalElement -import info.nightscout.androidaps.plugins.general.tidepool.elements.BaseElement -import info.nightscout.androidaps.plugins.general.tidepool.elements.BloodGlucoseElement -import info.nightscout.androidaps.plugins.general.tidepool.elements.BolusElement -import info.nightscout.androidaps.plugins.general.tidepool.elements.ProfileElement -import info.nightscout.androidaps.plugins.general.tidepool.elements.SensorGlucoseElement -import info.nightscout.androidaps.plugins.general.tidepool.elements.WizardElement -import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolStatus -import info.nightscout.androidaps.plugins.general.tidepool.utils.GsonInstance +import info.nightscout.androidaps.plugins.sync.tidepool.elements.BasalElement +import info.nightscout.androidaps.plugins.sync.tidepool.elements.BaseElement +import info.nightscout.androidaps.plugins.sync.tidepool.elements.BloodGlucoseElement +import info.nightscout.androidaps.plugins.sync.tidepool.elements.BolusElement +import info.nightscout.androidaps.plugins.sync.tidepool.elements.ProfileElement +import info.nightscout.androidaps.plugins.sync.tidepool.elements.SensorGlucoseElement +import info.nightscout.androidaps.plugins.sync.tidepool.elements.WizardElement +import info.nightscout.androidaps.plugins.sync.tidepool.events.EventTidepoolStatus +import info.nightscout.androidaps.plugins.sync.tidepool.utils.GsonInstance import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.T import info.nightscout.rx.bus.RxBus diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BasalElement.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/BasalElement.kt similarity index 93% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BasalElement.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/BasalElement.kt index 272a039300..f4efd137c9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BasalElement.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/BasalElement.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.elements +package info.nightscout.androidaps.plugins.sync.tidepool.elements import com.google.gson.annotations.Expose import info.nightscout.androidaps.interfaces.Profile diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BaseElement.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/BaseElement.kt similarity index 91% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BaseElement.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/BaseElement.kt index 49aa6eae18..59df1bec63 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BaseElement.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/BaseElement.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.elements +package info.nightscout.androidaps.plugins.sync.tidepool.elements import com.google.gson.annotations.Expose import info.nightscout.androidaps.utils.DateUtil diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BloodGlucoseElement.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/BloodGlucoseElement.kt similarity index 91% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BloodGlucoseElement.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/BloodGlucoseElement.kt index 58f209f6dd..e753a72906 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BloodGlucoseElement.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/BloodGlucoseElement.kt @@ -1,9 +1,9 @@ -package info.nightscout.androidaps.plugins.general.tidepool.elements +package info.nightscout.androidaps.plugins.sync.tidepool.elements import com.google.gson.annotations.Expose import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.extensions.toMainUnit +import info.nightscout.plugins.sync.nsclient.extensions.toMainUnit import info.nightscout.androidaps.utils.DateUtil import java.util.* diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BolusElement.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/BolusElement.kt similarity index 89% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BolusElement.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/BolusElement.kt index 6c3a05b0eb..436468e30a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BolusElement.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/BolusElement.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.elements +package info.nightscout.androidaps.plugins.sync.tidepool.elements import com.google.gson.annotations.Expose import info.nightscout.androidaps.database.entities.Bolus diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/ProfileElement.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/ProfileElement.kt similarity index 94% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/ProfileElement.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/ProfileElement.kt index 12c7b55fd7..96cc274dd4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/ProfileElement.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/ProfileElement.kt @@ -1,13 +1,12 @@ -package info.nightscout.androidaps.plugins.general.tidepool.elements +package info.nightscout.androidaps.plugins.sync.tidepool.elements import com.google.gson.annotations.Expose import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch import info.nightscout.androidaps.interfaces.Profile -import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader +import info.nightscout.androidaps.plugins.sync.tidepool.comm.TidepoolUploader import info.nightscout.androidaps.utils.DateUtil import java.util.* -import kotlin.collections.ArrayList class ProfileElement(ps: EffectiveProfileSwitch, serialNumber: String, dateUtil: DateUtil) : BaseElement(ps.timestamp, UUID.nameUUIDFromBytes(("AAPS-profile" + ps.timestamp).toByteArray()).toString(), dateUtil) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/SensorGlucoseElement.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/SensorGlucoseElement.kt similarity index 93% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/SensorGlucoseElement.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/SensorGlucoseElement.kt index cd9538018a..757a12ce39 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/SensorGlucoseElement.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/SensorGlucoseElement.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.elements +package info.nightscout.androidaps.plugins.sync.tidepool.elements import com.google.gson.annotations.Expose import info.nightscout.androidaps.database.entities.GlucoseValue diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/WizardElement.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/WizardElement.kt similarity index 96% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/WizardElement.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/WizardElement.kt index a6615e45a3..c2e1b724f5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/WizardElement.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/elements/WizardElement.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.elements +package info.nightscout.androidaps.plugins.sync.tidepool.elements import com.google.gson.annotations.Expose import info.nightscout.androidaps.database.entities.Bolus diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolStatus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/events/EventTidepoolStatus.kt similarity index 90% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolStatus.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/events/EventTidepoolStatus.kt index a3abc3f8ed..7addb83399 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolStatus.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/events/EventTidepoolStatus.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.events +package info.nightscout.androidaps.plugins.sync.tidepool.events import info.nightscout.rx.events.Event import java.text.SimpleDateFormat diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolUpdateGUI.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/events/EventTidepoolUpdateGUI.kt similarity index 54% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolUpdateGUI.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/events/EventTidepoolUpdateGUI.kt index 5e4f76717e..7e6e2b796b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolUpdateGUI.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/events/EventTidepoolUpdateGUI.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.events +package info.nightscout.androidaps.plugins.sync.tidepool.events import info.nightscout.rx.events.Event diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/AuthReplyMessage.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/AuthReplyMessage.kt similarity index 89% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/AuthReplyMessage.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/AuthReplyMessage.kt index 57fa45c691..99edf8eeed 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/AuthReplyMessage.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/AuthReplyMessage.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.messages +package info.nightscout.androidaps.plugins.sync.tidepool.messages import com.google.gson.annotations.Expose import com.google.gson.annotations.SerializedName diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/AuthRequestMessage.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/AuthRequestMessage.kt similarity index 88% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/AuthRequestMessage.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/AuthRequestMessage.kt index 9c7c191864..afa62e5894 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/AuthRequestMessage.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/AuthRequestMessage.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.messages +package info.nightscout.androidaps.plugins.sync.tidepool.messages import info.nightscout.androidaps.R import info.nightscout.shared.sharedPreferences.SP diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/BaseMessage.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/BaseMessage.kt similarity index 72% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/BaseMessage.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/BaseMessage.kt index d9dfc97b38..b3ae854bf2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/BaseMessage.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/BaseMessage.kt @@ -1,6 +1,6 @@ -package info.nightscout.androidaps.plugins.general.tidepool.messages +package info.nightscout.androidaps.plugins.sync.tidepool.messages -import info.nightscout.androidaps.plugins.general.tidepool.utils.GsonInstance +import info.nightscout.androidaps.plugins.sync.tidepool.utils.GsonInstance import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/CloseDatasetRequestMessage.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/CloseDatasetRequestMessage.kt similarity index 67% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/CloseDatasetRequestMessage.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/CloseDatasetRequestMessage.kt index f8f6780971..3bc3f5b670 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/CloseDatasetRequestMessage.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/CloseDatasetRequestMessage.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.messages +package info.nightscout.androidaps.plugins.sync.tidepool.messages import com.google.gson.annotations.Expose diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/DatasetReplyMessage.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/DatasetReplyMessage.kt similarity index 94% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/DatasetReplyMessage.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/DatasetReplyMessage.kt index 9db4595c16..295f917a24 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/DatasetReplyMessage.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/DatasetReplyMessage.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.messages +package info.nightscout.androidaps.plugins.sync.tidepool.messages class DatasetReplyMessage { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/OpenDatasetRequestMessage.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/OpenDatasetRequestMessage.kt similarity index 91% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/OpenDatasetRequestMessage.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/OpenDatasetRequestMessage.kt index 631a0259bd..2de5434727 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/OpenDatasetRequestMessage.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/OpenDatasetRequestMessage.kt @@ -1,8 +1,8 @@ -package info.nightscout.androidaps.plugins.general.tidepool.messages +package info.nightscout.androidaps.plugins.sync.tidepool.messages import com.google.gson.annotations.Expose import info.nightscout.androidaps.BuildConfig -import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader +import info.nightscout.androidaps.plugins.sync.tidepool.comm.TidepoolUploader import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.T import java.util.* diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/UploadReplyMessage.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/UploadReplyMessage.kt similarity index 52% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/UploadReplyMessage.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/UploadReplyMessage.kt index 2054eb237a..459c63ec6e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/UploadReplyMessage.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/messages/UploadReplyMessage.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.messages +package info.nightscout.androidaps.plugins.sync.tidepool.messages class UploadReplyMessage { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/utils/GsonInstance.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/utils/GsonInstance.kt similarity index 85% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/utils/GsonInstance.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/utils/GsonInstance.kt index 2c7ceb81d0..5c383f3440 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/utils/GsonInstance.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/utils/GsonInstance.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.utils +package info.nightscout.androidaps.plugins.sync.tidepool.utils import com.google.gson.Gson import com.google.gson.GsonBuilder diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/utils/RateLimit.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/utils/RateLimit.kt similarity index 93% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/utils/RateLimit.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/utils/RateLimit.kt index 40480ec784..482c01797d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/utils/RateLimit.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sync/tidepool/utils/RateLimit.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.tidepool.utils +package info.nightscout.androidaps.plugins.sync.tidepool.utils import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.T diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveWorker.kt b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveWorker.kt index 6396ce66b4..6c5563fff4 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveWorker.kt @@ -16,12 +16,9 @@ import info.nightscout.androidaps.BuildConfig import info.nightscout.androidaps.R import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.extensions.buildDeviceStatus import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.CommandQueue -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.IobCobCalculator -import info.nightscout.interfaces.LocalAlertUtils import info.nightscout.androidaps.interfaces.Loop import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.ProfileFunction @@ -32,6 +29,9 @@ import info.nightscout.androidaps.queue.commands.Command import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.extensions.buildDeviceStatus +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.LocalAlertUtils import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventProfileSwitchChanged import info.nightscout.rx.logging.AAPSLogger diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt index 88a8d5b5ec..da21e80294 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt @@ -7,28 +7,23 @@ import android.net.Uri import android.provider.Settings import androidx.appcompat.app.AppCompatActivity import dagger.android.HasAndroidInjector -import info.nightscout.interfaces.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.dialogs.ProfileSwitchDialog import info.nightscout.androidaps.events.EventPumpStatusChanged import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.AndroidPermission import info.nightscout.androidaps.interfaces.CommandQueue -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.ConfigBuilder import info.nightscout.androidaps.interfaces.ImportExportPrefs -import info.nightscout.interfaces.PluginType import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesFragment import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin -import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange import info.nightscout.androidaps.plugins.pump.omnipod.dash.OmnipodDashPumpPlugin import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientStatus import info.nightscout.androidaps.setupwizard.elements.SWBreak import info.nightscout.androidaps.setupwizard.elements.SWButton import info.nightscout.androidaps.setupwizard.elements.SWEditEncryptedPassword @@ -36,7 +31,6 @@ import info.nightscout.androidaps.setupwizard.elements.SWEditIntNumber import info.nightscout.androidaps.setupwizard.elements.SWEditNumber import info.nightscout.androidaps.setupwizard.elements.SWEditNumberWithUnits import info.nightscout.androidaps.setupwizard.elements.SWEditString -import info.nightscout.androidaps.setupwizard.elements.SWEditUrl import info.nightscout.androidaps.setupwizard.elements.SWFragment import info.nightscout.androidaps.setupwizard.elements.SWHtmlLink import info.nightscout.androidaps.setupwizard.elements.SWInfoText @@ -47,6 +41,10 @@ import info.nightscout.androidaps.setupwizard.events.EventSWUpdate import info.nightscout.androidaps.utils.CryptoUtil import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.extensions.isRunningTest +import info.nightscout.interfaces.AndroidPermission +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.Constants +import info.nightscout.interfaces.PluginType import info.nightscout.plugins.profile.ProfileFragment import info.nightscout.plugins.profile.ProfilePlugin import info.nightscout.rx.bus.RxBus @@ -68,7 +66,6 @@ class SWDefinition @Inject constructor( private val objectivesPlugin: ObjectivesPlugin, private val configBuilder: ConfigBuilder, private val loopPlugin: LoopPlugin, - private val nsClientPlugin: NSClientPlugin, private val importExportPrefs: ImportExportPrefs, private val androidPermission: AndroidPermission, private val cryptoUtil: CryptoUtil, @@ -89,167 +86,196 @@ class SWDefinition @Inject constructor( } private val screenSetupWizard = SWScreen(injector, R.string.nav_setupwizard) - .add(SWInfoText(injector) - .label(R.string.welcometosetupwizard)) + .add( + SWInfoText(injector) + .label(R.string.welcometosetupwizard) + ) private val screenEula = SWScreen(injector, R.string.end_user_license_agreement) .skippable(false) - .add(SWInfoText(injector) - .label(R.string.end_user_license_agreement_text)) + .add( + SWInfoText(injector) + .label(R.string.end_user_license_agreement_text) + ) .add(SWBreak(injector)) .add(SWButton(injector) - .text(R.string.end_user_license_agreement_i_understand) - .visibility { !sp.getBoolean(R.string.key_i_understand, false) } - .action { - sp.putBoolean(R.string.key_i_understand, true) - rxBus.send(EventSWUpdate(false)) - }) + .text(R.string.end_user_license_agreement_i_understand) + .visibility { !sp.getBoolean(R.string.key_i_understand, false) } + .action { + sp.putBoolean(R.string.key_i_understand, true) + rxBus.send(EventSWUpdate(false)) + }) .visibility { !sp.getBoolean(R.string.key_i_understand, false) } .validator { sp.getBoolean(R.string.key_i_understand, false) } private val screenUnits = SWScreen(injector, R.string.units) .skippable(false) - .add(SWRadioButton(injector) - .option(R.array.unitsArray, R.array.unitsValues) - .preferenceId(R.string.key_units).label(R.string.units) - .comment(R.string.setupwizard_units_prompt)) + .add( + SWRadioButton(injector) + .option(R.array.unitsArray, R.array.unitsValues) + .preferenceId(R.string.key_units).label(R.string.units) + .comment(R.string.setupwizard_units_prompt) + ) .validator { sp.contains(R.string.key_units) } private val displaySettings = SWScreen(injector, R.string.wear_display_settings) .skippable(false) - .add(SWEditNumberWithUnits(injector, Constants.LOW_MARK * Constants.MGDL_TO_MMOLL, 3.0, 8.0) - .preferenceId(R.string.key_low_mark) - .updateDelay(5) - .label(R.string.low_mark) - .comment(R.string.low_mark_comment)) + .add( + SWEditNumberWithUnits(injector, Constants.LOW_MARK * Constants.MGDL_TO_MMOLL, 3.0, 8.0) + .preferenceId(R.string.key_low_mark) + .updateDelay(5) + .label(R.string.low_mark) + .comment(R.string.low_mark_comment) + ) .add(SWBreak(injector)) - .add(SWEditNumberWithUnits(injector, Constants.HIGH_MARK * Constants.MGDL_TO_MMOLL, 5.0, 20.0) - .preferenceId(R.string.key_high_mark) - .updateDelay(5) - .label(R.string.high_mark) - .comment(R.string.high_mark_comment)) + .add( + SWEditNumberWithUnits(injector, Constants.HIGH_MARK * Constants.MGDL_TO_MMOLL, 5.0, 20.0) + .preferenceId(R.string.key_high_mark) + .updateDelay(5) + .label(R.string.high_mark) + .comment(R.string.high_mark_comment) + ) private val screenPermissionWindow = SWScreen(injector, R.string.permission) .skippable(false) - .add(SWInfoText(injector) - .label(rh.gs(R.string.need_system_window_permission))) + .add( + SWInfoText(injector) + .label(rh.gs(R.string.need_system_window_permission)) + ) .add(SWBreak(injector)) .add(SWButton(injector) - .text(R.string.askforpermission) - .visibility { !Settings.canDrawOverlays(activity) } - .action { activity.startActivity(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + activity.packageName))) }) + .text(R.string.askforpermission) + .visibility { !Settings.canDrawOverlays(activity) } + .action { activity.startActivity(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + activity.packageName))) }) .visibility { !Settings.canDrawOverlays(activity) } .validator { Settings.canDrawOverlays(activity) } private val screenPermissionBattery = SWScreen(injector, R.string.permission) .skippable(false) - .add(SWInfoText(injector) - .label(rh.gs(R.string.need_whitelisting, rh.gs(R.string.app_name)))) + .add( + SWInfoText(injector) + .label(rh.gs(R.string.need_whitelisting, rh.gs(R.string.app_name))) + ) .add(SWBreak(injector)) .add(SWButton(injector) - .text(R.string.askforpermission) - .visibility { androidPermission.permissionNotGranted(context, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) } - .action { androidPermission.askForPermission(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) }) + .text(R.string.askforpermission) + .visibility { androidPermission.permissionNotGranted(context, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) } + .action { androidPermission.askForPermission(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) }) .visibility { androidPermission.permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) } .validator { !androidPermission.permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) } private val screenPermissionBt = SWScreen(injector, R.string.permission) .skippable(false) - .add(SWInfoText(injector) - .label(rh.gs(R.string.need_location_permission))) + .add( + SWInfoText(injector) + .label(rh.gs(R.string.need_location_permission)) + ) .add(SWBreak(injector)) .add(SWButton(injector) - .text(R.string.askforpermission) - .visibility { androidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) } - .action { androidPermission.askForPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) }) + .text(R.string.askforpermission) + .visibility { androidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) } + .action { androidPermission.askForPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) }) .visibility { androidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) } .validator { !androidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) } private val screenPermissionStore = SWScreen(injector, R.string.permission) .skippable(false) - .add(SWInfoText(injector) - .label(rh.gs(R.string.need_storage_permission))) + .add( + SWInfoText(injector) + .label(rh.gs(R.string.need_storage_permission)) + ) .add(SWBreak(injector)) .add(SWButton(injector) - .text(R.string.askforpermission) - .visibility { androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) } - .action { androidPermission.askForPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) + .text(R.string.askforpermission) + .visibility { androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) } + .action { androidPermission.askForPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }) .visibility { androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) } .validator { !androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) } private val screenImport = SWScreen(injector, R.string.nav_import) - .add(SWInfoText(injector) - .label(R.string.storedsettingsfound)) - .add(SWBreak(injector)) - .add(SWButton(injector) - .text(R.string.nav_import) - .action { importExportPrefs.importSharedPreferences(activity) }) - .visibility { importExportPrefs.prefsFileExists() && !androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) } - private val screenNsClient = SWScreen(injector, R.string.nsclientinternal_title) - .skippable(true) - .add(SWInfoText(injector) - .label(R.string.nsclientinfotext)) - .add(SWBreak(injector)) - .add(SWButton(injector) - .text(R.string.enable_nsclient) - .action { - configBuilder.performPluginSwitch(nsClientPlugin, true, PluginType.GENERAL) - rxBus.send(EventSWUpdate(true)) - } - .visibility { !nsClientPlugin.isEnabled() }) - .add(SWEditUrl(injector) - .preferenceId(R.string.key_nsclientinternal_url) - .updateDelay(5) - .label(R.string.nsclientinternal_url_title) - .comment(R.string.nsclientinternal_url_dialogmessage)) - .add(SWEditString(injector) - .validator { text: String -> text.length >= 12 } - .preferenceId(R.string.key_nsclientinternal_api_secret) - .updateDelay(5) - .label(R.string.nsclientinternal_secret_dialogtitle) - .comment(R.string.nsclientinternal_secret_dialogmessage)) - .add(SWBreak(injector)) - .add(SWEventListener(injector, EventNSClientStatus::class.java) - .label(R.string.status) - .initialStatus(nsClientPlugin.status) + .add( + SWInfoText(injector) + .label(R.string.storedsettingsfound) ) - .validator { nsClientPlugin.nsClientService?.isConnected == true && nsClientPlugin.nsClientService?.hasWriteAuth == true } - .visibility { !(nsClientPlugin.nsClientService?.isConnected == true && nsClientPlugin.nsClientService?.hasWriteAuth == true) } + .add(SWBreak(injector)) + .add(SWButton(injector) + .text(R.string.nav_import) + .action { importExportPrefs.importSharedPreferences(activity) }) + .visibility { importExportPrefs.prefsFileExists() && !androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) } + private val screenNsClient = SWScreen(injector, R.string.configbuilder_sync) + .skippable(true) + .add( + SWPlugin(injector, this) + .option(PluginType.SYNC, R.string.configbuilder_sync_description) + .makeVisible(false) + .label(R.string.configbuilder_insulin) + ) + .add(SWBreak(injector)) + .add( + SWInfoText(injector) + .label(R.string.syncinfotext) + ) + .add(SWBreak(injector)) + .add( + SWEventListener(injector, EventNSClientStatus::class.java) + .label(R.string.status) + .initialStatus(activePlugin.firstActiveSync?.status ?: "") + ) + .validator { activePlugin.firstActiveSync?.connected == true && activePlugin.firstActiveSync?.hasWritePermission == true } private val screenPatientName = SWScreen(injector, R.string.patient_name) .skippable(true) - .add(SWInfoText(injector) - .label(R.string.patient_name_summary)) - .add(SWEditString(injector) - .validator(SWTextValidator(String::isNotEmpty)) - .preferenceId(R.string.key_patient_name)) + .add( + SWInfoText(injector) + .label(R.string.patient_name_summary) + ) + .add( + SWEditString(injector) + .validator(SWTextValidator(String::isNotEmpty)) + .preferenceId(R.string.key_patient_name) + ) private val privacy = SWScreen(injector, R.string.privacy_settings) .skippable(true) - .add(SWInfoText(injector) - .label(R.string.privacy_summary)) - .add(SWPreference(injector, this) - .option(R.xml.pref_datachoices) + .add( + SWInfoText(injector) + .label(R.string.privacy_summary) + ) + .add( + SWPreference(injector, this) + .option(R.xml.pref_datachoices) ) private val screenMasterPassword = SWScreen(injector, R.string.master_password) .skippable(false) - .add(SWInfoText(injector) - .label(R.string.master_password)) - .add(SWEditEncryptedPassword(injector, cryptoUtil) - .preferenceId(R.string.key_master_password)) + .add( + SWInfoText(injector) + .label(R.string.master_password) + ) + .add( + SWEditEncryptedPassword(injector, cryptoUtil) + .preferenceId(R.string.key_master_password) + ) .add(SWBreak(injector)) - .add(SWInfoText(injector) - .label(R.string.master_password_summary)) + .add( + SWInfoText(injector) + .label(R.string.master_password_summary) + ) .validator { !cryptoUtil.checkPassword("", sp.getString(R.string.key_master_password, "")) } private val screenAge = SWScreen(injector, R.string.patientage) .skippable(false) .add(SWBreak(injector)) - .add(SWRadioButton(injector) - .option(R.array.ageArray, R.array.ageValues) - .preferenceId(R.string.key_age) - .label(R.string.patientage) - .comment(R.string.patientage_summary)) + .add( + SWRadioButton(injector) + .option(R.array.ageArray, R.array.ageValues) + .preferenceId(R.string.key_age) + .label(R.string.patientage) + .comment(R.string.patientage_summary) + ) .add(SWBreak(injector)) - .add(SWEditNumber(injector, 3.0, 0.1, 25.0) - .preferenceId(R.string.key_treatmentssafety_maxbolus) - .updateDelay(5) - .label(R.string.treatmentssafety_maxbolus_title) - .comment(R.string.common_values)) - .add(SWEditIntNumber(injector, 48, 1, 100) - .preferenceId(R.string.key_treatmentssafety_maxcarbs) - .updateDelay(5) - .label(R.string.treatmentssafety_maxcarbs_title) - .comment(R.string.common_values)) + .add( + SWEditNumber(injector, 3.0, 0.1, 25.0) + .preferenceId(R.string.key_treatmentssafety_maxbolus) + .updateDelay(5) + .label(R.string.treatmentssafety_maxbolus_title) + .comment(R.string.common_values) + ) + .add( + SWEditIntNumber(injector, 48, 1, 100) + .preferenceId(R.string.key_treatmentssafety_maxcarbs) + .updateDelay(5) + .label(R.string.treatmentssafety_maxcarbs_title) + .comment(R.string.common_values) + ) .validator { sp.contains(R.string.key_age) && sp.getDouble(R.string.key_treatmentssafety_maxbolus, 0.0) > 0 @@ -257,23 +283,31 @@ class SWDefinition @Inject constructor( } private val screenInsulin = SWScreen(injector, R.string.configbuilder_insulin) .skippable(false) - .add(SWPlugin(injector, this) - .option(PluginType.INSULIN, R.string.configbuilder_insulin_description) - .makeVisible(false) - .label(R.string.configbuilder_insulin)) + .add( + SWPlugin(injector, this) + .option(PluginType.INSULIN, R.string.configbuilder_insulin_description) + .makeVisible(false) + .label(R.string.configbuilder_insulin) + ) .add(SWBreak(injector)) - .add(SWInfoText(injector) - .label(R.string.diawarning)) + .add( + SWInfoText(injector) + .label(R.string.diawarning) + ) private val screenBgSource = SWScreen(injector, R.string.configbuilder_bgsource) .skippable(false) - .add(SWPlugin(injector, this) - .option(PluginType.BGSOURCE, R.string.configbuilder_bgsource_description) - .label(R.string.configbuilder_bgsource)) + .add( + SWPlugin(injector, this) + .option(PluginType.BGSOURCE, R.string.configbuilder_bgsource_description) + .label(R.string.configbuilder_bgsource) + ) .add(SWBreak(injector)) private val screenLocalProfile = SWScreen(injector, R.string.localprofile) .skippable(false) - .add(SWFragment(injector, this) - .add(ProfileFragment())) + .add( + SWFragment(injector, this) + .add(ProfileFragment()) + ) .validator { profilePlugin.profile?.getDefaultProfile()?.let { ProfileSealed.Pure(it).isValid("StartupWizard", activePlugin.activePump, config, rh, rxBus, hardLimits, false).isValid } ?: false @@ -281,22 +315,26 @@ class SWDefinition @Inject constructor( .visibility { profilePlugin.isEnabled() } private val screenProfileSwitch = SWScreen(injector, R.string.careportal_profileswitch) .skippable(false) - .add(SWInfoText(injector) - .label(R.string.profileswitch_ismissing)) + .add( + SWInfoText(injector) + .label(R.string.profileswitch_ismissing) + ) .add(SWButton(injector) - .text(R.string.doprofileswitch) - .action { ProfileSwitchDialog().show(activity.supportFragmentManager, "ProfileSwitchDialog") }) + .text(R.string.doprofileswitch) + .action { ProfileSwitchDialog().show(activity.supportFragmentManager, "ProfileSwitchDialog") }) .validator { profileFunction.getRequestedProfile() != null } .visibility { profileFunction.getRequestedProfile() == null } private val screenPump = SWScreen(injector, R.string.configbuilder_pump) .skippable(false) - .add(SWPlugin(injector, this) - .option(PluginType.PUMP, R.string.configbuilder_pump_description) - .label(R.string.configbuilder_pump)) + .add( + SWPlugin(injector, this) + .option(PluginType.PUMP, R.string.configbuilder_pump_description) + .label(R.string.configbuilder_pump) + ) .add(SWBreak(injector)) .add(SWInfoText(injector) - .label(R.string.setupwizard_pump_pump_not_initialized) - .visibility { !isPumpInitialized() }) + .label(R.string.setupwizard_pump_pump_not_initialized) + .visibility { !isPumpInitialized() }) .add( // Omnipod Eros only SWInfoText(injector) .label(R.string.setupwizard_pump_waiting_for_riley_link_connection) @@ -309,15 +347,15 @@ class SWDefinition @Inject constructor( .label(R.string.setupwizard_pump_riley_link_status) .visibility { activePlugin.activePump is OmnipodErosPumpPlugin }) .add(SWButton(injector) - .text(R.string.readstatus) - .action { commandQueue.readStatus(rh.gs(R.string.clicked_connect_to_pump), null) } - .visibility { - // Hide for Omnipod, because as we don't require a Pod to be paired in the setup wizard, - // Getting the status might not be possible - activePlugin.activePump !is OmnipodErosPumpPlugin && activePlugin.activePump !is OmnipodDashPumpPlugin - }) + .text(R.string.readstatus) + .action { commandQueue.readStatus(rh.gs(R.string.clicked_connect_to_pump), null) } + .visibility { + // Hide for Omnipod, because as we don't require a Pod to be paired in the setup wizard, + // Getting the status might not be possible + activePlugin.activePump !is OmnipodErosPumpPlugin && activePlugin.activePump !is OmnipodDashPumpPlugin + }) .add(SWEventListener(injector, EventPumpStatusChanged::class.java) - .visibility { activePlugin.activePump !is OmnipodErosPumpPlugin && activePlugin.activePump !is OmnipodDashPumpPlugin }) + .visibility { activePlugin.activePump !is OmnipodErosPumpPlugin && activePlugin.activePump !is OmnipodDashPumpPlugin }) .validator { isPumpInitialized() } private fun isPumpInitialized(): Boolean { @@ -333,54 +371,74 @@ class SWDefinition @Inject constructor( private val screenAps = SWScreen(injector, R.string.configbuilder_aps) .skippable(false) - .add(SWInfoText(injector) - .label(R.string.setupwizard_aps_description)) + .add( + SWInfoText(injector) + .label(R.string.setupwizard_aps_description) + ) .add(SWBreak(injector)) - .add(SWPlugin(injector, this) - .option(PluginType.APS, R.string.configbuilder_aps_description) - .label(R.string.configbuilder_aps)) + .add( + SWPlugin(injector, this) + .option(PluginType.APS, R.string.configbuilder_aps_description) + .label(R.string.configbuilder_aps) + ) .add(SWBreak(injector)) - .add(SWHtmlLink(injector) - .label("https://openaps.readthedocs.io/en/latest/")) + .add( + SWHtmlLink(injector) + .label("https://openaps.readthedocs.io/en/latest/") + ) .add(SWBreak(injector)) private val screenApsMode = SWScreen(injector, R.string.apsmode_title) .skippable(false) - .add(SWRadioButton(injector) - .option(R.array.aps_modeArray, R.array.aps_modeValues) - .preferenceId(R.string.key_aps_mode).label(R.string.apsmode_title) - .comment(R.string.setupwizard_preferred_aps_mode)) + .add( + SWRadioButton(injector) + .option(R.array.aps_modeArray, R.array.aps_modeValues) + .preferenceId(R.string.key_aps_mode).label(R.string.apsmode_title) + .comment(R.string.setupwizard_preferred_aps_mode) + ) .validator { sp.contains(R.string.key_aps_mode) } private val screenLoop = SWScreen(injector, R.string.configbuilder_loop) .skippable(false) - .add(SWInfoText(injector) - .label(R.string.setupwizard_loop_description)) + .add( + SWInfoText(injector) + .label(R.string.setupwizard_loop_description) + ) .add(SWBreak(injector)) .add(SWButton(injector) - .text(R.string.enableloop) - .action { - configBuilder.performPluginSwitch(loopPlugin, true, PluginType.LOOP) - rxBus.send(EventSWUpdate(true)) - } - .visibility { !loopPlugin.isEnabled() }) + .text(R.string.enableloop) + .action { + configBuilder.performPluginSwitch(loopPlugin, true, PluginType.LOOP) + rxBus.send(EventSWUpdate(true)) + } + .visibility { !loopPlugin.isEnabled() }) .validator { loopPlugin.isEnabled() } .visibility { !loopPlugin.isEnabled() && config.APS } private val screenSensitivity = SWScreen(injector, R.string.configbuilder_sensitivity) .skippable(false) - .add(SWInfoText(injector) - .label(R.string.setupwizard_sensitivity_description)) - .add(SWHtmlLink(injector) - .label(R.string.setupwizard_sensitivity_url)) + .add( + SWInfoText(injector) + .label(R.string.setupwizard_sensitivity_description) + ) + .add( + SWHtmlLink(injector) + .label(R.string.setupwizard_sensitivity_url) + ) .add(SWBreak(injector)) - .add(SWPlugin(injector, this) - .option(PluginType.SENSITIVITY, R.string.configbuilder_sensitivity_description) - .label(R.string.configbuilder_sensitivity)) + .add( + SWPlugin(injector, this) + .option(PluginType.SENSITIVITY, R.string.configbuilder_sensitivity_description) + .label(R.string.configbuilder_sensitivity) + ) private val getScreenObjectives = SWScreen(injector, R.string.objectives) .skippable(false) - .add(SWInfoText(injector) - .label(R.string.startobjective)) + .add( + SWInfoText(injector) + .label(R.string.startobjective) + ) .add(SWBreak(injector)) - .add(SWFragment(injector, this) - .add(ObjectivesFragment())) + .add( + SWFragment(injector, this) + .add(ObjectivesFragment()) + ) .validator { objectivesPlugin.objectives[ObjectivesPlugin.FIRST_OBJECTIVE].isStarted } .visibility { !objectivesPlugin.objectives[ObjectivesPlugin.FIRST_OBJECTIVE].isStarted && config.APS } diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.kt b/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.kt index db98314bb5..7431530e70 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.kt @@ -11,19 +11,19 @@ import info.nightscout.androidaps.activities.NoSplashAppCompatActivity import info.nightscout.androidaps.databinding.ActivitySetupwizardBinding import info.nightscout.androidaps.events.EventPumpStatusChanged import info.nightscout.androidaps.logging.UserEntryLogger -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange +import info.nightscout.androidaps.plugins.sync.nsShared.events.EventNSClientStatus import info.nightscout.androidaps.setupwizard.elements.SWItem import info.nightscout.androidaps.setupwizard.events.EventSWUpdate import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.interfaces.locale.LocaleHelper.update -import info.nightscout.plugins.profile.ProfilePlugin import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.events.EventProfileStoreChanged import info.nightscout.rx.events.EventProfileSwitchChanged import info.nightscout.shared.sharedPreferences.SP import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject import kotlin.math.max import kotlin.math.min @@ -31,7 +31,6 @@ import kotlin.math.min class SetupWizardActivity : NoSplashAppCompatActivity() { @Inject lateinit var injector: HasAndroidInjector - @Inject lateinit var profilePlugin: ProfilePlugin @Inject lateinit var swDefinition: SWDefinition @Inject lateinit var sp: SP @Inject lateinit var fabricPrivacy: FabricPrivacy @@ -76,39 +75,33 @@ class SetupWizardActivity : NoSplashAppCompatActivity() { override fun onResume() { super.onResume() swDefinition.activity = this - disposable.add(rxBus + disposable += rxBus .toObservable(EventPumpStatusChanged::class.java) .observeOn(aapsSchedulers.main) .subscribe({ updateButtons() }, fabricPrivacy::logException) - ) - disposable.add(rxBus + disposable += rxBus .toObservable(EventRileyLinkDeviceStatusChange::class.java) .observeOn(aapsSchedulers.main) .subscribe({ updateButtons() }, fabricPrivacy::logException) - ) - disposable.add(rxBus + disposable += rxBus .toObservable(EventNSClientStatus::class.java) .observeOn(aapsSchedulers.main) .subscribe({ updateButtons() }, fabricPrivacy::logException) - ) - disposable.add(rxBus + disposable += rxBus .toObservable(EventProfileSwitchChanged::class.java) .observeOn(aapsSchedulers.main) .subscribe({ updateButtons() }, fabricPrivacy::logException) - ) - disposable.add(rxBus + disposable += rxBus .toObservable(EventProfileStoreChanged::class.java) .observeOn(aapsSchedulers.main) .subscribe({ updateButtons() }, fabricPrivacy::logException) - ) - disposable.add(rxBus + disposable += rxBus .toObservable(EventSWUpdate::class.java) .observeOn(aapsSchedulers.main) .subscribe({ event: EventSWUpdate -> - if (event.redraw) generateLayout() - updateButtons() - }, fabricPrivacy::logException) - ) + if (event.redraw) generateLayout() + updateButtons() + }, fabricPrivacy::logException) updateButtons() } diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWPlugin.kt b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWPlugin.kt index 3fc9940884..0f9afd9240 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWPlugin.kt @@ -60,7 +60,7 @@ class SWPlugin(injector: HasAndroidInjector, private val definition: SWDefinitio val p = pluginsInCategory[i] rdBtn.id = View.generateViewId() rdBtn.text = p.name - if (p.isEnabled(pType!!)) { + if (p.isEnabled()) { rdBtn.isChecked = true selectedPlugin = p } @@ -78,7 +78,7 @@ class SWPlugin(injector: HasAndroidInjector, private val definition: SWDefinitio val plugin = rb.tag as PluginBase plugin.setPluginEnabled(pType!!, rb.isChecked) plugin.setFragmentVisible(pType!!, rb.isChecked && makeVisible) - configBuilderPlugin.processOnEnabledCategoryChanged(plugin, pType) + configBuilderPlugin.processOnEnabledCategoryChanged(plugin, pType!!) configBuilderPlugin.storeSettings("SetupWizard") rxBus.send(EventConfigBuilderChange()) rxBus.send(EventSWUpdate(false)) diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/DeviceStatusExtension.kt b/app/src/main/java/info/nightscout/androidaps/utils/extensions/DeviceStatusExtension.kt similarity index 99% rename from core/src/main/java/info/nightscout/androidaps/extensions/DeviceStatusExtension.kt rename to app/src/main/java/info/nightscout/androidaps/utils/extensions/DeviceStatusExtension.kt index a7b12a061f..ffc3ddc8ac 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/DeviceStatusExtension.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/extensions/DeviceStatusExtension.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.extensions +package info.nightscout.androidaps.utils.extensions import android.os.Build import info.nightscout.androidaps.database.entities.DeviceStatus diff --git a/app/src/main/java/info/nightscout/androidaps/utils/extensions/TherapyEventExtension.kt b/app/src/main/java/info/nightscout/androidaps/utils/extensions/TherapyEventExtension.kt new file mode 100644 index 0000000000..f11572cfd1 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/extensions/TherapyEventExtension.kt @@ -0,0 +1,12 @@ +package info.nightscout.androidaps.utils.extensions + +import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.plugins.sync.nsclient.data.NSMbg + +fun therapyEventFromNsMbg(mbg: NSMbg) = + TherapyEvent( + type = TherapyEvent.Type.FINGER_STICK_BG_VALUE, //convert Mbg to finger stick because is coming from "entries" collection + timestamp = mbg.date, + glucose = mbg.mbg, + glucoseUnit = TherapyEvent.GlucoseUnit.MGDL + ) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/tabs/TabPageAdapter.kt b/app/src/main/java/info/nightscout/androidaps/utils/tabs/TabPageAdapter.kt index 2743c7f21c..90d3bd65bd 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/tabs/TabPageAdapter.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/tabs/TabPageAdapter.kt @@ -4,6 +4,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.viewpager2.adapter.FragmentStateAdapter import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.PluginFragment import java.util.* class TabPageAdapter(private val activity: AppCompatActivity) : FragmentStateAdapter(activity) { @@ -13,6 +14,7 @@ class TabPageAdapter(private val activity: AppCompatActivity) : FragmentStateAda override fun getItemCount(): Int = visibleFragmentList.size override fun createFragment(position: Int): Fragment = activity.supportFragmentManager.fragmentFactory.instantiate(ClassLoader.getSystemClassLoader(), visibleFragmentList[position].pluginDescription.fragmentClass ?: Fragment::class.java.name) + .also { if (it is PluginFragment) it.plugin = getPluginAt(position) } fun getPluginAt(position: Int): PluginBase = visibleFragmentList[position] diff --git a/app/src/main/java/info/nightscout/androidaps/workflow/PreparePredictionsWorker.kt b/app/src/main/java/info/nightscout/androidaps/workflow/PreparePredictionsWorker.kt index 81fc9752dd..eb70bcc3e3 100644 --- a/app/src/main/java/info/nightscout/androidaps/workflow/PreparePredictionsWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/workflow/PreparePredictionsWorker.kt @@ -6,19 +6,19 @@ import androidx.work.WorkerParameters import androidx.work.workDataOf import dagger.android.HasAndroidInjector import info.nightscout.androidaps.database.AppRepository -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.Loop import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ResourceHelper -import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus import info.nightscout.androidaps.plugins.general.overview.OverviewData import info.nightscout.androidaps.plugins.general.overview.OverviewMenus import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface import info.nightscout.androidaps.plugins.general.overview.graphExtensions.GlucoseValueDataPoint import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries +import info.nightscout.androidaps.plugins.sync.nsclient.data.ProcessedDeviceStatusData import info.nightscout.androidaps.receivers.DataWorkerStorage import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.T +import info.nightscout.interfaces.Config import info.nightscout.rx.bus.RxBus import java.util.Calendar import javax.inject.Inject @@ -36,7 +36,7 @@ class PreparePredictionsWorker( @Inject lateinit var repository: AppRepository @Inject lateinit var rxBus: RxBus @Inject lateinit var config: Config - @Inject lateinit var nsDeviceStatus: NSDeviceStatus + @Inject lateinit var processedDeviceStatusData: ProcessedDeviceStatusData @Inject lateinit var loop: Loop @Inject lateinit var overviewMenus: OverviewMenus @Inject lateinit var dataWorkerStorage: DataWorkerStorage @@ -56,7 +56,7 @@ class PreparePredictionsWorker( val data = dataWorkerStorage.pickupObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) as PreparePredictionsData? ?: return Result.failure(workDataOf("Error" to "missing input data")) - val apsResult = if (config.APS) loop.lastRun?.constraintsProcessed else nsDeviceStatus.getAPSResult(injector) + val apsResult = if (config.APS) loop.lastRun?.constraintsProcessed else processedDeviceStatusData.getAPSResult(injector) val predictionsAvailable = if (config.APS) loop.lastRun?.request?.hasPredictions == true else config.NSCLIENT val menuChartSettings = overviewMenus.setting // align to hours diff --git a/app/src/main/res/layout/ns_client_fragment.xml b/app/src/main/res/layout/ns_client_fragment.xml index 199f4075d8..91016d058e 100644 --- a/app/src/main/res/layout/ns_client_fragment.xml +++ b/app/src/main/res/layout/ns_client_fragment.xml @@ -3,7 +3,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - tools:context=".plugins.general.nsclient.NSClientFragment"> + tools:context=".plugins.sync.nsShared.NSClientFragment"> protection absorption_category_settings ns_temporary_target_last_sync - temporary_target_new_data_id ns_glucose_value_last_sync - ns_glucose_value_new_data_id ns_food_last_sync ns_therapy_event_last_sync bolussnooze_dia_divisor @@ -45,14 +43,14 @@ ns_bolus_last_synced_id ns_device_status_last_synced_id ns_temporary_basal_last_synced_id - ns_temporary_basal_new_data_id ns_extended_bolus_last_synced_id - ns_extended_bolus_new_data_id profile_switch_last_synced_id ns_effective_profile_switch_last_synced_id ns_offline_event_last_synced_id ns_profile_store_last_synced_timestamp ns_sync_slow + nsclient_token + nsclientv2_lastmodified last_cleanup_run Treatments safety Max allowed bolus [U] @@ -68,6 +66,7 @@ Used for configuring the active plugins Learning program Activate or deactivate the implementation triggering the loop. + Synchronizes your data with Nightscout using v3 API Synchronizes your data with Nightscout State of the algorithm in 2017 Most recent algorithm for advanced users @@ -111,6 +110,8 @@ Which APS algorithm should make therapy adjustments? General These are some general plugins you might find useful. + Synchronization + Data upload and synchronization plugins. Which constraints are applied? Constraints Loop @@ -236,6 +237,8 @@ Executing Virtual pump settings Upload status to NS + NSClientV3 + NSV3 NSClient NSCI URL: @@ -247,6 +250,9 @@ NS API secret NS API secret Enter NS API secret (min 12 chars) + NS access token + NS access token + Access token generated on NS admin page (min 17 chars) Deliver now Clear queue Show queue @@ -438,7 +444,7 @@ Percentage calculation Loop enabled APS selected - NSClient has write permission + Synchronization service has write permission Closed mode enabled Maximal IOB set properly BG available from selected source @@ -584,7 +590,6 @@ Calculations included in the Wizard result: Display Settings General Settings - Enable NSClient Welcome to setup wizard. It will guide you through the setup process\n Read status Skip setup wizard @@ -592,7 +597,7 @@ startupwizard_processed Sensitivity plugin is used for sensitivity detection and COB calculation. For more info visit: https://androidaps.readthedocs.io/en/latest/Configuration/Sensitivity-detection-and-COB.html - NSClient handles connection to Nightscout. You can skip this part now but you will not be able to pass objectives until you set it up. + Synchronize data to the cloud. You can skip this part now but you will not be able to pass objectives until you set it up. Please remember: new insulin profiles require DIA at least 5h. DIA 5–6h on new profile is equal to DIA 3h on old insulin profiles. Select one from availables algorithms. They are sorted from oldest to newest. Newer algorithm is usually more powerful and more aggressive. Thus if you are new looper you may probably start with AMA and not with latest one. Do not forget to read the OpenAPS documentation and configure it before use. Please configure your RileyLink below. After selecting a RileyLink, it will be possible to continue setup once the RileyLink status is \"Connected\". This might take a minute.\n diff --git a/app/src/main/res/xml/pref_nsclientinternal.xml b/app/src/main/res/xml/pref_nsclientinternal.xml index bf7ef4678e..fff4e93e93 100644 --- a/app/src/main/res/xml/pref_nsclientinternal.xml +++ b/app/src/main/res/xml/pref_nsclientinternal.xml @@ -26,6 +26,15 @@ validate:minLength="12" validate:testType="minLength"/> + + diff --git a/app/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt b/app/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt index 78bd435dc5..d3aeee31d2 100644 --- a/app/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt +++ b/app/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt @@ -6,7 +6,7 @@ import dagger.android.HasAndroidInjector import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.database.embedments.InsulinConfiguration import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch -import info.nightscout.androidaps.extensions.pureProfileFromJson +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.IobCobCalculator diff --git a/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.kt b/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.kt index e160d208fa..ecf9dbe9ab 100644 --- a/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.kt @@ -132,7 +132,7 @@ class ConstraintsCheckerTest : TestBaseWithProfile() { insightDbHelper = InsightDbHelper(insightDatabaseDao) danaPump = DanaPump(aapsLogger, sp, dateUtil, injector) hardLimits = HardLimits(aapsLogger, rxBus, sp, rh, context, repository) - objectivesPlugin = ObjectivesPlugin(injector, aapsLogger, rh, activePlugin, sp, config, dateUtil, uel) + objectivesPlugin = ObjectivesPlugin(injector, aapsLogger, rh, activePlugin, sp, config) comboPlugin = ComboPlugin(injector, aapsLogger, rxBus, rh, profileFunction, sp, commandQueue, context, pumpSync, dateUtil, ruffyScripter) danaRPlugin = DanaRPlugin(injector, aapsLogger, aapsSchedulers, rxBus, context, rh, constraintChecker, activePlugin, sp, commandQueue, danaPump, dateUtil, fabricPrivacy, pumpSync) danaRSPlugin = diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPluginTest.kt b/app/src/test/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPluginTest.kt index 421635b0b6..da937f0aa9 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPluginTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPluginTest.kt @@ -7,10 +7,10 @@ import info.nightscout.androidaps.TestBase import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.shared.sharedPreferences.SP import org.junit.Assert import org.junit.Before @@ -40,7 +40,7 @@ class ObjectivesPluginTest : TestBase() { } @Before fun prepareMock() { - objectivesPlugin = ObjectivesPlugin(injector, aapsLogger, rh, activePlugin, sp, config, dateUtil, uel) + objectivesPlugin = ObjectivesPlugin(injector, aapsLogger, rh, activePlugin, sp, config) objectivesPlugin.onStart() `when`(rh.gs(R.string.objectivenotstarted, 9)).thenReturn("Objective 9 not started") `when`(rh.gs(R.string.objectivenotstarted, 8)).thenReturn("Objective 8 not started") diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenancePluginTest.kt b/app/src/test/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenancePluginTest.kt index 4fa83a6062..581d87d18c 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenancePluginTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenancePluginTest.kt @@ -6,7 +6,7 @@ import info.nightscout.androidaps.TestBase import info.nightscout.interfaces.BuildHelper import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.ResourceHelper -import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus +import info.nightscout.androidaps.plugins.sync.nsclient.data.NSSettingsStatus import info.nightscout.plugins.general.maintenance.LoggerUtils import info.nightscout.shared.sharedPreferences.SP import org.junit.Assert diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegateTest.kt b/app/src/test/java/info/nightscout/androidaps/plugins/sync/nsclient/NsClientReceiverDelegateTest.kt similarity index 98% rename from app/src/test/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegateTest.kt rename to app/src/test/java/info/nightscout/androidaps/plugins/sync/nsclient/NsClientReceiverDelegateTest.kt index 0a98a6876d..cd70af2694 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegateTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/plugins/sync/nsclient/NsClientReceiverDelegateTest.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.general.nsclient +package info.nightscout.androidaps.plugins.sync.nsclient import android.content.Context import info.nightscout.androidaps.R diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/tidepool/comm/SessionTest.kt b/app/src/test/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/SessionTest.kt similarity index 80% rename from app/src/test/java/info/nightscout/androidaps/plugins/general/tidepool/comm/SessionTest.kt rename to app/src/test/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/SessionTest.kt index f7af8d995b..4d9f61fbe0 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/tidepool/comm/SessionTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/plugins/sync/tidepool/comm/SessionTest.kt @@ -1,12 +1,13 @@ -package info.nightscout.androidaps.plugins.general.tidepool.comm +package info.nightscout.androidaps.plugins.sync.tidepool.comm -import info.nightscout.androidaps.plugins.general.tidepool.messages.AuthReplyMessage -import info.nightscout.androidaps.plugins.general.tidepool.messages.DatasetReplyMessage +import info.nightscout.androidaps.plugins.sync.tidepool.messages.AuthReplyMessage +import info.nightscout.androidaps.plugins.sync.tidepool.messages.DatasetReplyMessage import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Test class SessionTest { + @Test fun populateBody() { val session = Session("", "", null) diff --git a/automation/build.gradle b/automation/build.gradle index 9e341a7efb..320fffc2b2 100644 --- a/automation/build.gradle +++ b/automation/build.gradle @@ -1,11 +1,14 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" android { diff --git a/automation/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt b/automation/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt index 869f17b626..edb91245cc 100644 --- a/automation/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt +++ b/automation/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt @@ -4,7 +4,7 @@ import dagger.android.AndroidInjector import dagger.android.HasAndroidInjector import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.extensions.pureProfileFromJson +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.Profile diff --git a/build.gradle b/build.gradle index 84bd819423..c4af65575f 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,7 @@ buildscript { work_version = '2.7.1' tink_version = '1.5.0' json_version = '20220320' + serialization_version = '1.4.1' joda_version = '2.12.1' junit_version = '4.13.2' diff --git a/core/allopen_dependencies.gradle b/core/allopen_dependencies.gradle new file mode 100644 index 0000000000..002600b6c0 --- /dev/null +++ b/core/allopen_dependencies.gradle @@ -0,0 +1,4 @@ +allOpen { + // allows mocking for classes w/o directly opening them for release builds + annotation 'info.nightscout.androidaps.annotations.OpenForTesting' +} diff --git a/core/android_dependencies.gradle b/core/android_dependencies.gradle index 0997a3fcc9..43ea057c27 100644 --- a/core/android_dependencies.gradle +++ b/core/android_dependencies.gradle @@ -19,10 +19,6 @@ android { debug { testCoverageEnabled(project.hasProperty('coverage')) } -// firebaseDisable { -// System.setProperty("disableFirebase", "true") -// ext.enableCrashlytics = false -// } } sourceSets { diff --git a/core/android_module_dependencies.gradle b/core/android_module_dependencies.gradle index ffe86f3cc1..bcc7585a61 100644 --- a/core/android_module_dependencies.gradle +++ b/core/android_module_dependencies.gradle @@ -40,8 +40,3 @@ dependencies { implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" } - -allOpen { - // allows mocking for classes w/o directly opening them for release builds - annotation 'info.nightscout.androidaps.annotations.OpenForTesting' -} diff --git a/core/build.gradle b/core/build.gradle index 65c02d25e4..9e6e9006c2 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,13 +1,16 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-parcelize' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-parcelize' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: 'core_dependencies.gradle' apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" diff --git a/core/src/main/java/info/nightscout/androidaps/dialogs/ProfileViewerDialog.kt b/core/src/main/java/info/nightscout/androidaps/dialogs/ProfileViewerDialog.kt index 2c22ed8729..e91acf1be7 100644 --- a/core/src/main/java/info/nightscout/androidaps/dialogs/ProfileViewerDialog.kt +++ b/core/src/main/java/info/nightscout/androidaps/dialogs/ProfileViewerDialog.kt @@ -9,22 +9,22 @@ import android.view.Window import android.view.WindowManager import dagger.android.HasAndroidInjector import dagger.android.support.DaggerDialogFragment -import info.nightscout.interfaces.Constants import info.nightscout.androidaps.core.R import info.nightscout.androidaps.core.databinding.DialogProfileviewerBinding import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper -import info.nightscout.androidaps.extensions.getCustomizedName -import info.nightscout.androidaps.extensions.pureProfileFromJson import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.HardLimits +import info.nightscout.androidaps.utils.extensions.getCustomizedName +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.Constants import info.nightscout.interfaces.utils.HtmlHelper import info.nightscout.rx.bus.RxBus import org.json.JSONObject @@ -63,8 +63,10 @@ class ProfileViewerDialog : DaggerDialogFragment() { // onDestroyView. private val binding get() = _binding!! - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View { + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { // load data from bundle (savedInstanceState ?: arguments)?.let { bundle -> time = bundle.getLong("time", 0) @@ -107,8 +109,8 @@ class ProfileViewerDialog : DaggerDialogFragment() { binding.datelayout.visibility = View.VISIBLE } - Mode.CUSTOM_PROFILE -> { - profile = pureProfileFromJson(JSONObject(customProfileJson), dateUtil)?.let { ProfileSealed.Pure(it)} + Mode.CUSTOM_PROFILE -> { + profile = pureProfileFromJson(JSONObject(customProfileJson), dateUtil)?.let { ProfileSealed.Pure(it) } profile2 = null profileName = customProfileName date = "" @@ -116,15 +118,15 @@ class ProfileViewerDialog : DaggerDialogFragment() { } Mode.PROFILE_COMPARE -> { - profile = pureProfileFromJson(JSONObject(customProfileJson), dateUtil)?.let { ProfileSealed.Pure(it)} - profile2 = pureProfileFromJson(JSONObject(customProfileJson2), dateUtil)?.let { ProfileSealed.Pure(it)} + profile = pureProfileFromJson(JSONObject(customProfileJson), dateUtil)?.let { ProfileSealed.Pure(it) } + profile2 = pureProfileFromJson(JSONObject(customProfileJson2), dateUtil)?.let { ProfileSealed.Pure(it) } profileName = customProfileName binding.headerIcon.setImageResource(R.drawable.ic_compare_profiles) date = "" binding.datelayout.visibility = View.GONE } - Mode.DB_PROFILE -> { + Mode.DB_PROFILE -> { //val profileList = databaseHelper.getProfileSwitchData(time, true) val profileList = repository.getAllProfileSwitches().blockingGet() profile = if (profileList.isNotEmpty()) ProfileSealed.PS(profileList[0]) else null @@ -237,12 +239,15 @@ class ProfileViewerDialog : DaggerDialogFragment() { prev1 = val1 prev2 = val2 } - s.append(formatColors( - " ∑ ", - profile1.baseBasalSum(), - profile2.baseBasalSum(), - DecimalFormat("0.00"), - rh.gs(R.string.insulin_unit_shortname))) + s.append( + formatColors( + " ∑ ", + profile1.baseBasalSum(), + profile2.baseBasalSum(), + DecimalFormat("0.00"), + rh.gs(R.string.insulin_unit_shortname) + ) + ) return HtmlHelper.fromHtml(s.toString()) } @@ -293,8 +298,18 @@ class ProfileViewerDialog : DaggerDialogFragment() { val val1h = profile1.getTargetHighMgdlTimeFromMidnight(hour * 60 * 60) val val2l = profile2.getTargetLowMgdlTimeFromMidnight(hour * 60 * 60) val val2h = profile2.getTargetHighMgdlTimeFromMidnight(hour * 60 * 60) - val txt1 = dateUtil.formatHHMM(hour * 60 * 60) + " " + Profile.toUnitsString(val1l, val1l * Constants.MGDL_TO_MMOLL, units) + " - " + Profile.toUnitsString(val1h, val1h * Constants.MGDL_TO_MMOLL, units) + " " + units.asText - val txt2 = dateUtil.formatHHMM(hour * 60 * 60) + " " + Profile.toUnitsString(val2l, val2l * Constants.MGDL_TO_MMOLL, units) + " - " + Profile.toUnitsString(val2h, val2h * Constants.MGDL_TO_MMOLL, units) + " " + units.asText + val txt1 = + dateUtil.formatHHMM(hour * 60 * 60) + " " + Profile.toUnitsString(val1l, val1l * Constants.MGDL_TO_MMOLL, units) + " - " + Profile.toUnitsString( + val1h, + val1h * Constants.MGDL_TO_MMOLL, + units + ) + " " + units.asText + val txt2 = + dateUtil.formatHHMM(hour * 60 * 60) + " " + Profile.toUnitsString(val2l, val2l * Constants.MGDL_TO_MMOLL, units) + " - " + Profile.toUnitsString( + val2h, + val2h * Constants.MGDL_TO_MMOLL, + units + ) + " " + units.asText if (val1l != prev1l || val1h != prev1h || val2l != prev2l || val2h != prev2h) { s.append(formatColors(txt1, txt2)) s.append("
") diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/BolusExtension.kt b/core/src/main/java/info/nightscout/androidaps/extensions/BolusExtension.kt index f50728ad3b..9727aa143f 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/BolusExtension.kt +++ b/core/src/main/java/info/nightscout/androidaps/extensions/BolusExtension.kt @@ -21,61 +21,4 @@ fun Bolus.iobCalc(activePlugin: ActivePlugin, time: Long, dia: Double): Iob { fun Bolus.iobCalc(time: Long, localInsulin: LocalInsulin): Iob { if (!isValid || type == Bolus.Type.PRIMING ) return Iob() return localInsulin.iobCalcForTreatment(this, time) -} - -fun Bolus.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject = - JSONObject() - .put("eventType", if (type == Bolus.Type.SMB) TherapyEvent.Type.CORRECTION_BOLUS.text else TherapyEvent.Type.MEAL_BOLUS.text) - .put("insulin", amount) - .put("created_at", dateUtil.toISOString(timestamp)) - .put("date", timestamp) - .put("type", type.name) - .put("notes", notes) - .put("isValid", isValid) - .put("isSMB", type == Bolus.Type.SMB).also { - if (interfaceIDs.pumpId != null) it.put("pumpId", interfaceIDs.pumpId) - if (interfaceIDs.pumpType != null) it.put("pumpType", interfaceIDs.pumpType!!.name) - if (interfaceIDs.pumpSerial != null) it.put("pumpSerial", interfaceIDs.pumpSerial) - if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) - } - -/* - create fake object with nsID and isValid == false - */ -fun bolusFromNsIdForInvalidating(nsId: String): Bolus = - bolusFromJson( - JSONObject() - .put("mills", 1) - .put("insulin", -1.0) - .put("_id", nsId) - .put("isValid", false) - )!! - -fun bolusFromJson(jsonObject: JSONObject): Bolus? { - val timestamp = JsonHelper.safeGetLongAllowNull(jsonObject, "mills", null) ?: return null - val amount = JsonHelper.safeGetDoubleAllowNull(jsonObject, "insulin") ?: return null - val type = Bolus.Type.fromString(JsonHelper.safeGetString(jsonObject, "type")) - val isValid = JsonHelper.safeGetBoolean(jsonObject, "isValid", true) - val notes = JsonHelper.safeGetStringAllowNull(jsonObject, "notes", null) - val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null) ?: return null - val pumpId = JsonHelper.safeGetLongAllowNull(jsonObject, "pumpId", null) - val pumpType = InterfaceIDs.PumpType.fromString(JsonHelper.safeGetStringAllowNull(jsonObject, "pumpType", null)) - val pumpSerial = JsonHelper.safeGetStringAllowNull(jsonObject, "pumpSerial", null) - - if (timestamp == 0L) return null - if (amount == 0.0) return null - - return Bolus( - timestamp = timestamp, - amount = amount, - type = type, - notes = notes, - isValid = isValid, - ).also { - it.interfaceIDs.nightscoutId = id - it.interfaceIDs.pumpId = pumpId - it.interfaceIDs.pumpType = pumpType - it.interfaceIDs.pumpSerial = pumpSerial - } -} - +} \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/ExtendedBolusExtension.kt b/core/src/main/java/info/nightscout/androidaps/extensions/ExtendedBolusExtension.kt index 5cc69e1968..daf803471a 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/ExtendedBolusExtension.kt +++ b/core/src/main/java/info/nightscout/androidaps/extensions/ExtendedBolusExtension.kt @@ -1,20 +1,16 @@ package info.nightscout.androidaps.extensions import info.nightscout.androidaps.data.IobTotal -import info.nightscout.androidaps.interfaces.Profile -import info.nightscout.androidaps.database.embedments.InterfaceIDs import info.nightscout.androidaps.database.entities.Bolus import info.nightscout.androidaps.database.entities.ExtendedBolus import info.nightscout.androidaps.database.entities.TemporaryBasal -import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.interfaces.Insulin +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DecimalFormatter.to2Decimal -import info.nightscout.interfaces.utils.JsonHelper import info.nightscout.androidaps.utils.T -import org.json.JSONObject import kotlin.math.ceil import kotlin.math.max import kotlin.math.min @@ -50,84 +46,6 @@ fun ExtendedBolus.toTemporaryBasal(profile: Profile): TemporaryBasal = type = TemporaryBasal.Type.FAKE_EXTENDED ) -fun ExtendedBolus.toJson(isAdd: Boolean, profile: Profile, dateUtil: DateUtil): JSONObject = - if (isEmulatingTempBasal) - toTemporaryBasal(profile) - .toJson(isAdd, profile, dateUtil) - .put("extendedEmulated", toRealJson(isAdd, dateUtil)) - else toRealJson(isAdd, dateUtil) - -fun ExtendedBolus.toRealJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject = - JSONObject() - .put("created_at", dateUtil.toISOString(timestamp)) - .put("enteredBy", "openaps://" + "AndroidAPS") - .put("eventType", TherapyEvent.Type.COMBO_BOLUS.text) - .put("duration", T.msecs(duration).mins()) - .put("durationInMilliseconds", duration) - .put("splitNow", 0) - .put("splitExt", 100) - .put("enteredinsulin", amount) - .put("relative", rate) - .put("isValid", isValid) - .put("isEmulatingTempBasal", isEmulatingTempBasal) - .also { - if (interfaceIDs.pumpId != null) it.put("pumpId", interfaceIDs.pumpId) - if (interfaceIDs.endId != null) it.put("endId", interfaceIDs.endId) - if (interfaceIDs.pumpType != null) it.put("pumpType", interfaceIDs.pumpType!!.name) - if (interfaceIDs.pumpSerial != null) it.put("pumpSerial", interfaceIDs.pumpSerial) - if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) - } - -/* - create fake object with nsID and isValid == false - */ -fun extendedBolusFromNsIdForInvalidating(nsId: String): ExtendedBolus = - extendedBolusFromJson( - JSONObject() - .put("mills", 1) - .put("amount", -1.0) - .put("enteredinsulin", -1.0) - .put("duration", -1.0) - .put("splitNow", 0) - .put("splitExt", 100) - .put("_id", nsId) - .put("isValid", false) - )!! - -fun extendedBolusFromJson(jsonObject: JSONObject): ExtendedBolus? { - val timestamp = JsonHelper.safeGetLongAllowNull(jsonObject, "mills", null) ?: return null - if (JsonHelper.safeGetIntAllowNull(jsonObject, "splitNow") != 0) return null - if (JsonHelper.safeGetIntAllowNull(jsonObject, "splitExt") != 100) return null - val amount = JsonHelper.safeGetDoubleAllowNull(jsonObject, "enteredinsulin") ?: return null - val duration = JsonHelper.safeGetLongAllowNull(jsonObject, "duration") ?: return null - val durationInMilliseconds = JsonHelper.safeGetLongAllowNull(jsonObject, "durationInMilliseconds") - val isValid = JsonHelper.safeGetBoolean(jsonObject, "isValid", true) - val isEmulatingTempBasal = JsonHelper.safeGetBoolean(jsonObject, "isEmulatingTempBasal", false) - val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null) ?: return null - val pumpId = JsonHelper.safeGetLongAllowNull(jsonObject, "pumpId", null) - val endPumpId = JsonHelper.safeGetLongAllowNull(jsonObject, "endId", null) - val pumpType = InterfaceIDs.PumpType.fromString(JsonHelper.safeGetStringAllowNull(jsonObject, "pumpType", null)) - val pumpSerial = JsonHelper.safeGetStringAllowNull(jsonObject, "pumpSerial", null) - - if (timestamp == 0L) return null - if (duration == 0L && durationInMilliseconds == 0L) return null - if (amount == 0.0) return null - - return ExtendedBolus( - timestamp = timestamp, - amount = amount, - duration = durationInMilliseconds ?: T.mins(duration).msecs(), - isEmulatingTempBasal = isEmulatingTempBasal, - isValid = isValid - ).also { - it.interfaceIDs.nightscoutId = id - it.interfaceIDs.pumpId = pumpId - it.interfaceIDs.endId = endPumpId - it.interfaceIDs.pumpType = pumpType - it.interfaceIDs.pumpSerial = pumpSerial - } -} - fun ExtendedBolus.iobCalc(time: Long, profile: Profile, insulinInterface: Insulin): IobTotal { val result = IobTotal(time) val realDuration = getPassedDurationToTimeInMinutes(time) @@ -156,7 +74,15 @@ fun ExtendedBolus.iobCalc(time: Long, profile: Profile, insulinInterface: Insuli return result } -fun ExtendedBolus.iobCalc(time: Long, profile: Profile, lastAutosensResult: AutosensResult, exercise_mode: Boolean, half_basal_exercise_target: Int, isTempTarget: Boolean, insulinInterface: Insulin): IobTotal { +fun ExtendedBolus.iobCalc( + time: Long, + profile: Profile, + lastAutosensResult: AutosensResult, + exercise_mode: Boolean, + half_basal_exercise_target: Int, + isTempTarget: Boolean, + insulinInterface: Insulin +): IobTotal { val result = IobTotal(time) val realDuration = getPassedDurationToTimeInMinutes(time) var sensitivityRatio = lastAutosensResult.ratio diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/ProfileSwitchExtension.kt b/core/src/main/java/info/nightscout/androidaps/extensions/ProfileSwitchExtension.kt index cf03d76f96..c4f297fc20 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/ProfileSwitchExtension.kt +++ b/core/src/main/java/info/nightscout/androidaps/extensions/ProfileSwitchExtension.kt @@ -1,19 +1,18 @@ -package info.nightscout.androidaps.extensions +package info.nightscout.androidaps.utils.extensions import info.nightscout.interfaces.Constants import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.data.PureProfile -import info.nightscout.androidaps.database.embedments.InterfaceIDs import info.nightscout.androidaps.database.entities.ProfileSwitch -import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.extensions.blockFromJsonArray +import info.nightscout.androidaps.extensions.targetBlockFromJsonArray import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DecimalFormatter.to2Decimal import info.nightscout.interfaces.utils.JsonHelper import info.nightscout.androidaps.utils.T import org.json.JSONObject -import java.util.* +import java.util.TimeZone fun List.isPSEvent5minBack(time: Long): Boolean { for (event in this) { @@ -27,88 +26,23 @@ fun List.isPSEvent5minBack(time: Long): Boolean { return false } -fun ProfileSwitch.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject = - JSONObject() - .put("timeshift", timeshift) - .put("percentage", percentage) - .put("duration", T.msecs(duration).mins()) - .put("profile", getCustomizedName()) - .put("originalProfileName", profileName) - .put("originalDuration", duration) - .put("created_at", dateUtil.toISOString(timestamp)) - .put("enteredBy", "openaps://" + "AndroidAPS") - .put("isValid", isValid) - .put("eventType", TherapyEvent.Type.PROFILE_SWITCH.text) - .also { // remove customization to store original profileJson in toPureNsJson call - timeshift = 0 - percentage = 100 - } - .put("profileJson", ProfileSealed.PS(this).toPureNsJson(dateUtil).toString()) - .also { - if (interfaceIDs.pumpId != null) it.put("pumpId", interfaceIDs.pumpId) - if (interfaceIDs.pumpType != null) it.put("pumpType", interfaceIDs.pumpType!!.name) - if (interfaceIDs.pumpSerial != null) it.put("pumpSerial", interfaceIDs.pumpSerial) - if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) - } - -/* NS PS -{ - "_id":"608ffa268db0676196a772d7", - "enteredBy":"undefined", - "eventType":"Profile Switch", - "duration":10, - "profile":"LocalProfile0", - "created_at":"2021-05-03T13:26:58.537Z", - "utcOffset":0, - "mills":1620048418537, - "mgdl":98 -} - */ -fun profileSwitchFromJson(jsonObject: JSONObject, dateUtil: DateUtil, activePlugin: ActivePlugin): ProfileSwitch? { - val timestamp = JsonHelper.safeGetLongAllowNull(jsonObject, "mills", null) ?: return null - val duration = JsonHelper.safeGetLong(jsonObject, "duration") - val originalDuration = JsonHelper.safeGetLongAllowNull(jsonObject, "originalDuration") - val timeshift = JsonHelper.safeGetLong(jsonObject, "timeshift") - val percentage = JsonHelper.safeGetInt(jsonObject, "percentage", 100) - val isValid = JsonHelper.safeGetBoolean(jsonObject, "isValid", true) - val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null) - val profileName = JsonHelper.safeGetStringAllowNull(jsonObject, "profile", null) ?: return null - val originalProfileName = JsonHelper.safeGetStringAllowNull(jsonObject, "originalProfileName", null) - val profileJson = JsonHelper.safeGetStringAllowNull(jsonObject, "profileJson", null) - val pumpId = JsonHelper.safeGetLongAllowNull(jsonObject, "pumpId", null) - val pumpType = InterfaceIDs.PumpType.fromString(JsonHelper.safeGetStringAllowNull(jsonObject, "pumpType", null)) - val pumpSerial = JsonHelper.safeGetStringAllowNull(jsonObject, "pumpSerial", null) - - if (timestamp == 0L) return null - val pureProfile = - if (profileJson == null) { // entered through NS, no JSON attached - val profilePlugin = activePlugin.activeProfileSource - val store = profilePlugin.profile ?: return null - store.getSpecificProfile(profileName) ?: return null - } else pureProfileFromJson(JSONObject(profileJson), dateUtil) ?: return null - val profileSealed = ProfileSealed.Pure(pureProfile) - - return ProfileSwitch( - timestamp = timestamp, - basalBlocks = profileSealed.basalBlocks, - isfBlocks = profileSealed.isfBlocks, - icBlocks = profileSealed.icBlocks, - targetBlocks = profileSealed.targetBlocks, - glucoseUnit = ProfileSwitch.GlucoseUnit.fromConstant(profileSealed.units), - profileName = originalProfileName ?: profileName, - timeshift = timeshift, - percentage = percentage, - duration = originalDuration ?: T.mins(duration).msecs(), - insulinConfiguration = profileSealed.insulinConfiguration, - isValid = isValid - ).also { - it.interfaceIDs.nightscoutId = id - it.interfaceIDs.pumpId = pumpId - it.interfaceIDs.pumpType = pumpType - it.interfaceIDs.pumpSerial = pumpSerial +fun ProfileSwitch.getCustomizedName(): String { + var name: String = profileName + if (Constants.LOCAL_PROFILE == name) { + name = to2Decimal(ProfileSealed.PS(this).percentageBasalSum()) + "U " } + if (timeshift != 0L || percentage != 100) { + name += "($percentage%" + if (timeshift != 0L) name += "," + T.msecs(timeshift).hours() + "h" + name += ")" + } + return name } +fun ProfileSwitch.GlucoseUnit.Companion.fromConstant(units: GlucoseUnit): ProfileSwitch.GlucoseUnit = + if (units == GlucoseUnit.MGDL) ProfileSwitch.GlucoseUnit.MGDL + else ProfileSwitch.GlucoseUnit.MMOL + /** * Pure profile doesn't contain timestamp, percentage, timeshift, profileName */ @@ -140,21 +74,4 @@ fun pureProfileFromJson(jsonObject: JSONObject, dateUtil: DateUtil, defaultUnits } catch (ignored: Exception) { return null } -} - -fun ProfileSwitch.getCustomizedName(): String { - var name: String = profileName - if (Constants.LOCAL_PROFILE == name) { - name = to2Decimal(ProfileSealed.PS(this).percentageBasalSum()) + "U " - } - if (timeshift != 0L || percentage != 100) { - name += "($percentage%" - if (timeshift != 0L) name += "," + T.msecs(timeshift).hours() + "h" - name += ")" - } - return name -} - -fun ProfileSwitch.GlucoseUnit.Companion.fromConstant(units: GlucoseUnit): ProfileSwitch.GlucoseUnit = - if (units == GlucoseUnit.MGDL) ProfileSwitch.GlucoseUnit.MGDL - else ProfileSwitch.GlucoseUnit.MMOL +} \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/TemporaryBasalExtension.kt b/core/src/main/java/info/nightscout/androidaps/extensions/TemporaryBasalExtension.kt index f6101e576b..e06ff75f4b 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/TemporaryBasalExtension.kt +++ b/core/src/main/java/info/nightscout/androidaps/extensions/TemporaryBasalExtension.kt @@ -1,21 +1,16 @@ package info.nightscout.androidaps.extensions import info.nightscout.androidaps.data.IobTotal -import info.nightscout.androidaps.interfaces.Profile -import info.nightscout.androidaps.database.embedments.InterfaceIDs import info.nightscout.androidaps.database.entities.Bolus import info.nightscout.androidaps.database.entities.TemporaryBasal -import info.nightscout.androidaps.database.entities.TemporaryBasal.Type.Companion.fromString -import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.interfaces.Insulin +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DecimalFormatter.to0Decimal import info.nightscout.androidaps.utils.DecimalFormatter.to2Decimal -import info.nightscout.interfaces.utils.JsonHelper import info.nightscout.androidaps.utils.T -import org.json.JSONObject import kotlin.math.ceil import kotlin.math.max import kotlin.math.min @@ -62,69 +57,6 @@ fun TemporaryBasal.toStringFull(profile: Profile, dateUtil: DateUtil): String { } } -fun TemporaryBasal.toJson(isAdd: Boolean, profile: Profile, dateUtil: DateUtil): JSONObject = - JSONObject() - .put("created_at", dateUtil.toISOString(timestamp)) - .put("enteredBy", "openaps://" + "AndroidAPS") - .put("eventType", TherapyEvent.Type.TEMPORARY_BASAL.text) - .put("isValid", isValid) - .put("duration", T.msecs(duration).mins()) - .put("durationInMilliseconds", duration) // rounded duration leads to different basal IOB - .put("type", type.name) - .put("rate", convertedToAbsolute(timestamp, profile)) // generated by OpenAPS, for compatibility - .also { - if (isAbsolute) it.put("absolute", rate) - else it.put("percent", rate - 100) - if (interfaceIDs.pumpId != null) it.put("pumpId", interfaceIDs.pumpId) - if (interfaceIDs.endId != null) it.put("endId", interfaceIDs.endId) - if (interfaceIDs.pumpType != null) it.put("pumpType", interfaceIDs.pumpType!!.name) - if (interfaceIDs.pumpSerial != null) it.put("pumpSerial", interfaceIDs.pumpSerial) - if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) - } - -@Suppress("IfThenToElvis", "CascadeIf") -fun temporaryBasalFromJson(jsonObject: JSONObject): TemporaryBasal? { - val timestamp = JsonHelper.safeGetLongAllowNull(jsonObject, "mills", null) ?: return null - val percent = JsonHelper.safeGetDoubleAllowNull(jsonObject, "percent") - val absolute = JsonHelper.safeGetDoubleAllowNull(jsonObject, "absolute") - val duration = JsonHelper.safeGetLongAllowNull(jsonObject, "duration") ?: return null - val durationInMilliseconds = JsonHelper.safeGetLongAllowNull(jsonObject, "durationInMilliseconds") - val type = fromString(JsonHelper.safeGetString(jsonObject, "type")) - val isValid = JsonHelper.safeGetBoolean(jsonObject, "isValid", true) - val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null) ?: return null - val pumpId = JsonHelper.safeGetLongAllowNull(jsonObject, "pumpId", null) - val endPumpId = JsonHelper.safeGetLongAllowNull(jsonObject, "endId", null) - val pumpType = InterfaceIDs.PumpType.fromString(JsonHelper.safeGetStringAllowNull(jsonObject, "pumpType", null)) - val pumpSerial = JsonHelper.safeGetStringAllowNull(jsonObject, "pumpSerial", null) - - val rate: Double - val isAbsolute: Boolean - if (absolute != null) { - rate = absolute - isAbsolute = true - } else if (percent != null) { - rate = percent + 100 - isAbsolute = false - } else return null - if (duration == 0L && durationInMilliseconds == null) return null - if (timestamp == 0L) return null - - return TemporaryBasal( - timestamp = timestamp, - rate = rate, - duration = durationInMilliseconds ?: T.mins(duration).msecs(), - type = type, - isAbsolute = isAbsolute, - isValid = isValid - ).also { - it.interfaceIDs.nightscoutId = id - it.interfaceIDs.pumpId = pumpId - it.interfaceIDs.endId = endPumpId - it.interfaceIDs.pumpType = pumpType - it.interfaceIDs.pumpSerial = pumpSerial - } -} - fun TemporaryBasal.toStringShort(): String = if (isAbsolute || type == TemporaryBasal.Type.FAKE_EXTENDED) to2Decimal(rate) + "U/h" else "${to0Decimal(rate)}%" diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/TemporaryTargetExtension.kt b/core/src/main/java/info/nightscout/androidaps/extensions/TemporaryTargetExtension.kt index ae7a846565..4a221c85bc 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/TemporaryTargetExtension.kt +++ b/core/src/main/java/info/nightscout/androidaps/extensions/TemporaryTargetExtension.kt @@ -1,17 +1,13 @@ package info.nightscout.androidaps.extensions -import info.nightscout.interfaces.Constants import info.nightscout.androidaps.core.R import info.nightscout.androidaps.database.entities.TemporaryTarget -import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DecimalFormatter -import info.nightscout.interfaces.utils.JsonHelper -import info.nightscout.androidaps.utils.T -import info.nightscout.androidaps.interfaces.ResourceHelper -import org.json.JSONObject +import info.nightscout.interfaces.Constants import java.util.concurrent.TimeUnit fun TemporaryTarget.isInProgress(dateUtil: DateUtil): Boolean = @@ -32,71 +28,3 @@ fun TemporaryTarget.friendlyDescription(units: GlucoseUnit, rh: ResourceHelper): Profile.toTargetRangeString(lowTarget, highTarget, GlucoseUnit.MGDL, units) + units.asText + "@" + rh.gs(R.string.format_mins, TimeUnit.MILLISECONDS.toMinutes(duration)) + "(" + reason.text + ")" - -/* - create fake object with nsID and isValid == false - */ -fun temporaryTargetFromNsIdForInvalidating(nsId: String): TemporaryTarget = - temporaryTargetFromJson( - JSONObject() - .put("mills", 1) - .put("duration", -1) - .put("reason", "fake") - .put("_id", nsId) - .put("isValid", false) - )!! - -fun temporaryTargetFromJson(jsonObject: JSONObject): TemporaryTarget? { - val units = GlucoseUnit.fromText(JsonHelper.safeGetString(jsonObject, "units", Constants.MGDL)) - val timestamp = JsonHelper.safeGetLongAllowNull(jsonObject, "mills", null) ?: return null - val duration = JsonHelper.safeGetLongAllowNull(jsonObject, "duration", null) ?: return null - val durationInMilliseconds = JsonHelper.safeGetLongAllowNull(jsonObject, "durationInMilliseconds") - var low = JsonHelper.safeGetDouble(jsonObject, "targetBottom") - low = Profile.toMgdl(low, units) - var high = JsonHelper.safeGetDouble(jsonObject, "targetTop") - high = Profile.toMgdl(high, units) - val reasonString = if (duration != 0L) JsonHelper.safeGetStringAllowNull(jsonObject, "reason", null) - ?: return null else "" - // this string can be localized from NS, it will not work in this case CUSTOM will be used - val reason = TemporaryTarget.Reason.fromString(reasonString) - val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null) ?: return null - val isValid = JsonHelper.safeGetBoolean(jsonObject, "isValid", true) - - if (timestamp == 0L) return null - - if (duration > 0L) { - // not ending event - if (low < Constants.MIN_TT_MGDL) return null - if (low > Constants.MAX_TT_MGDL) return null - if (high < Constants.MIN_TT_MGDL) return null - if (high > Constants.MAX_TT_MGDL) return null - if (low > high) return null - } - val tt = TemporaryTarget( - timestamp = timestamp, - duration = durationInMilliseconds ?: T.mins(duration).msecs(), - reason = reason, - lowTarget = low, - highTarget = high, - isValid = isValid - ) - tt.interfaceIDs.nightscoutId = id - return tt -} - -fun TemporaryTarget.toJson(isAdd: Boolean, units: GlucoseUnit, dateUtil: DateUtil): JSONObject = - JSONObject() - .put("eventType", TherapyEvent.Type.TEMPORARY_TARGET.text) - .put("duration", T.msecs(duration).mins()) - .put("durationInMilliseconds", duration) - .put("isValid", isValid) - .put("created_at", dateUtil.toISOString(timestamp)) - .put("timestamp", timestamp) - .put("enteredBy", "AndroidAPS").also { - if (lowTarget > 0) it - .put("reason", reason.text) - .put("targetBottom", Profile.fromMgdlToUnits(lowTarget, units)) - .put("targetTop", Profile.fromMgdlToUnits(highTarget, units)) - .put("units", units.asText) - if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) - } diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/TherapyEventExtension.kt b/core/src/main/java/info/nightscout/androidaps/extensions/TherapyEventExtension.kt index 50bb28ca31..18350b849d 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/TherapyEventExtension.kt +++ b/core/src/main/java/info/nightscout/androidaps/extensions/TherapyEventExtension.kt @@ -1,27 +1,7 @@ package info.nightscout.androidaps.extensions -import info.nightscout.interfaces.Constants -import info.nightscout.androidaps.core.R import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.interfaces.GlucoseUnit -import info.nightscout.androidaps.interfaces.ResourceHelper -import info.nightscout.androidaps.plugins.general.nsclient.data.NSMbg -import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.interfaces.utils.JsonHelper -import info.nightscout.androidaps.utils.T -import org.json.JSONObject -import java.util.concurrent.TimeUnit - -fun TherapyEvent.age(useShortText: Boolean, rh: ResourceHelper, dateUtil: DateUtil): String { - val diff = dateUtil.computeDiff(timestamp, System.currentTimeMillis()) - var days = " " + rh.gs(R.string.days) + " " - var hours = " " + rh.gs(R.string.hours) + " " - if (useShortText) { - days = rh.gs(R.string.shortday) - hours = rh.gs(R.string.shorthour) - } - return diff[TimeUnit.DAYS].toString() + days + diff[TimeUnit.HOURS] + hours -} fun TherapyEvent.isOlderThan(hours: Double): Boolean { return getHoursFromStart() > hours @@ -31,81 +11,6 @@ fun TherapyEvent.getHoursFromStart(): Double { return (System.currentTimeMillis() - timestamp) / (60 * 60 * 1000.0) } -fun TherapyEvent.GlucoseUnit.toMainUnit(): GlucoseUnit = - if (this == TherapyEvent.GlucoseUnit.MGDL) GlucoseUnit.MGDL - else GlucoseUnit.MMOL - fun TherapyEvent.GlucoseUnit.Companion.fromConstant(units: GlucoseUnit): TherapyEvent.GlucoseUnit = if (units == GlucoseUnit.MGDL) TherapyEvent.GlucoseUnit.MGDL else TherapyEvent.GlucoseUnit.MMOL - -fun therapyEventFromNsMbg(mbg: NSMbg) = - TherapyEvent( - type = TherapyEvent.Type.FINGER_STICK_BG_VALUE, //convert Mbg to finger stick because is coming from "entries" collection - timestamp = mbg.date, - glucose = mbg.mbg, - glucoseUnit = TherapyEvent.GlucoseUnit.MGDL - ) -//.also { -// it.interfaceIDs.nightscoutId = mbg.id() // id will be different in treatments collection -//} - -/* - create fake object with nsID and isValid == false - */ - -fun therapyEventFromJson(jsonObject: JSONObject): TherapyEvent? { - val glucoseUnit = if (JsonHelper.safeGetString(jsonObject, "units", Constants.MGDL) == Constants.MGDL) TherapyEvent.GlucoseUnit.MGDL else TherapyEvent.GlucoseUnit.MMOL - val timestamp = JsonHelper.safeGetLongAllowNull(jsonObject, "mills", null) ?: return null - val type = TherapyEvent.Type.fromString(JsonHelper.safeGetString(jsonObject, "eventType", TherapyEvent.Type.NONE.text)) - val duration = JsonHelper.safeGetLong(jsonObject, "duration") - val durationInMilliseconds = JsonHelper.safeGetLongAllowNull(jsonObject, "durationInMilliseconds") - val glucose = JsonHelper.safeGetDoubleAllowNull(jsonObject, "glucose") - val glucoseType = TherapyEvent.MeterType.fromString(JsonHelper.safeGetString(jsonObject, "glucoseType")) - val enteredBy = JsonHelper.safeGetStringAllowNull(jsonObject, "enteredBy", null) - val note = JsonHelper.safeGetStringAllowNull(jsonObject, "notes", null) - val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null) ?: return null - val isValid = JsonHelper.safeGetBoolean(jsonObject, "isValid", true) - - if (timestamp == 0L) return null - - val te = TherapyEvent( - timestamp = timestamp, - duration = durationInMilliseconds ?: T.mins(duration).msecs(), - glucoseUnit = glucoseUnit, - type = type, - glucose = glucose, - glucoseType = glucoseType, - enteredBy = enteredBy, - note = note, - isValid = isValid - ) - te.interfaceIDs.nightscoutId = id - return te -} - -fun TherapyEvent.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject = - JSONObject() - .put("eventType", type.text) - .put("isValid", isValid) - .put("created_at", dateUtil.toISOString(timestamp)) - .put("enteredBy", enteredBy) - .put("units", if (glucoseUnit == TherapyEvent.GlucoseUnit.MGDL) Constants.MGDL else Constants.MMOL) - .also { - if (duration != 0L) it.put("duration", T.msecs(duration).mins()) - if (duration != 0L) it.put("durationInMilliseconds", duration) - if (note != null) it.put("notes", note) - if (glucose != null) it.put("glucose", glucose) - if (glucoseType != null) it.put("glucoseType", glucoseType!!.text) - if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) - if (type == TherapyEvent.Type.ANNOUNCEMENT) it.put("isAnnouncement", true) - } - -fun List.isTherapyEventEvent5minBack(time: Long): Boolean { - for (event in this) { - if (event.timestamp <= time && event.timestamp > time - T.mins(5).msecs()) { - return true - } - } - return false -} diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/ActivePlugin.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/ActivePlugin.kt index 55196b068b..5165d90c2d 100644 --- a/core/src/main/java/info/nightscout/androidaps/interfaces/ActivePlugin.kt +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/ActivePlugin.kt @@ -60,6 +60,17 @@ interface ActivePlugin { */ val activeIobCobCalculator: IobCobCalculator + /** + * Currently selected NsClient plugin + */ + val activeNsClient: NsClient? + + /** + * Currently selected Sync plugin + */ + val firstActiveSync: Sync? + val activeSyncs: ArrayList + /** * List of all registered plugins */ diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/DataSyncSelector.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/DataSyncSelector.kt index 7e6c49a8d0..bf215d0229 100644 --- a/core/src/main/java/info/nightscout/androidaps/interfaces/DataSyncSelector.kt +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/DataSyncSelector.kt @@ -29,22 +29,22 @@ interface DataSyncSelector { fun confirmLastBolusIdIfGreater(lastSynced: Long) fun changedBoluses() : List // Until NS v3 - fun processChangedBolusesCompat(): Boolean + fun processChangedBolusesCompat() fun confirmLastCarbsIdIfGreater(lastSynced: Long) fun changedCarbs() : List // Until NS v3 - fun processChangedCarbsCompat(): Boolean + fun processChangedCarbsCompat() fun confirmLastBolusCalculatorResultsIdIfGreater(lastSynced: Long) fun changedBolusCalculatorResults() : List // Until NS v3 - fun processChangedBolusCalculatorResultsCompat(): Boolean + fun processChangedBolusCalculatorResultsCompat() fun confirmLastTempTargetsIdIfGreater(lastSynced: Long) fun changedTempTargets() : List // Until NS v3 - fun processChangedTempTargetsCompat(): Boolean + fun processChangedTempTargetsCompat() fun confirmLastGlucoseValueIdIfGreater(lastSynced: Long) fun changedGlucoseValues() : List @@ -54,42 +54,42 @@ interface DataSyncSelector { fun confirmLastTherapyEventIdIfGreater(lastSynced: Long) fun changedTherapyEvents() : List // Until NS v3 - fun processChangedTherapyEventsCompat(): Boolean + fun processChangedTherapyEventsCompat() fun confirmLastFoodIdIfGreater(lastSynced: Long) fun changedFoods() : List // Until NS v3 - fun processChangedFoodsCompat(): Boolean + fun processChangedFoodsCompat() fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long) fun changedDeviceStatuses() : List // Until NS v3 - fun processChangedDeviceStatusesCompat(): Boolean + fun processChangedDeviceStatusesCompat() fun confirmLastTemporaryBasalIdIfGreater(lastSynced: Long) fun changedTemporaryBasals() : List // Until NS v3 - fun processChangedTemporaryBasalsCompat(): Boolean + fun processChangedTemporaryBasalsCompat() fun confirmLastExtendedBolusIdIfGreater(lastSynced: Long) fun changedExtendedBoluses() : List // Until NS v3 - fun processChangedExtendedBolusesCompat(): Boolean + fun processChangedExtendedBolusesCompat() fun confirmLastProfileSwitchIdIfGreater(lastSynced: Long) fun changedProfileSwitch() : List // Until NS v3 - fun processChangedProfileSwitchesCompat(): Boolean + fun processChangedProfileSwitchesCompat() fun confirmLastEffectiveProfileSwitchIdIfGreater(lastSynced: Long) fun changedEffectiveProfileSwitch() : List // Until NS v3 - fun processChangedEffectiveProfileSwitchesCompat(): Boolean + fun processChangedEffectiveProfileSwitchesCompat() fun confirmLastOfflineEventIdIfGreater(lastSynced: Long) fun changedOfflineEvents() : List // Until NS v3 - fun processChangedOfflineEventsCompat(): Boolean + fun processChangedOfflineEventsCompat() fun confirmLastProfileStore(lastSynced: Long) fun processChangedProfileStore() diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/NsClient.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/NsClient.kt new file mode 100644 index 0000000000..4380940eb0 --- /dev/null +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/NsClient.kt @@ -0,0 +1,30 @@ +package info.nightscout.androidaps.interfaces + +import android.text.Spanned +import org.json.JSONObject + +interface NsClient : Sync { + enum class Version { + NONE, V1, V3 + } + + val version: Version + val address: String + val nsClientService: NSClientService? + + fun pause(newState: Boolean) + fun resend(reason: String) + fun textLog(): Spanned + fun clearLog() + + fun updateLatestBgReceivedIfNewer(latestReceived: Long) + fun updateLatestTreatmentReceivedIfNewer(latestReceived: Long) + + fun resetToFullSync() + + interface NSClientService { + + fun dbAdd(collection: String, data: JSONObject, originalObject: Any, progress: String) + fun dbUpdate(collection: String, _id: String?, data: JSONObject?, originalObject: Any, progress: String) + } +} \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/PluginFragment.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/PluginFragment.kt new file mode 100644 index 0000000000..9bb570850e --- /dev/null +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/PluginFragment.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.interfaces + +interface PluginFragment { + var plugin: PluginBase? +} \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/ProfileStore.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/ProfileStore.kt index 6238e4ee1e..4909610ceb 100644 --- a/core/src/main/java/info/nightscout/androidaps/interfaces/ProfileStore.kt +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/ProfileStore.kt @@ -4,11 +4,11 @@ import androidx.collection.ArrayMap import dagger.android.HasAndroidInjector import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.data.PureProfile -import info.nightscout.androidaps.extensions.pureProfileFromJson import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.HardLimits -import info.nightscout.interfaces.utils.JsonHelper +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson import info.nightscout.interfaces.Config +import info.nightscout.interfaces.utils.JsonHelper import info.nightscout.rx.bus.RxBus import info.nightscout.rx.logging.AAPSLogger import org.json.JSONException @@ -100,5 +100,5 @@ class ProfileStore(val injector: HasAndroidInjector, val data: JSONObject, val d .asSequence() .map { profileName -> getSpecificProfile(profileName.toString()) } .map { pureProfile -> pureProfile?.let { ProfileSealed.Pure(pureProfile).isValid("allProfilesValid", activePlugin.activePump, config, rh, rxBus, hardLimits, false) } } - .all { it?.isValid == true} + .all { it?.isValid == true } } diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/Sync.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/Sync.kt new file mode 100644 index 0000000000..8139182f52 --- /dev/null +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/Sync.kt @@ -0,0 +1,8 @@ +package info.nightscout.androidaps.interfaces + +interface Sync { + + val hasWritePermission: Boolean + val connected: Boolean + val status: String +} \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/logging/UserEntryLogger.kt b/core/src/main/java/info/nightscout/androidaps/logging/UserEntryLogger.kt index cc84ee76ba..458573003d 100644 --- a/core/src/main/java/info/nightscout/androidaps/logging/UserEntryLogger.kt +++ b/core/src/main/java/info/nightscout/androidaps/logging/UserEntryLogger.kt @@ -6,13 +6,13 @@ import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.transactions.UserEntryTransaction +import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.userEntry.UserEntryMapper import info.nightscout.androidaps.utils.userEntry.ValueWithUnitMapper import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.logging.AAPSLogger import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign -import io.reactivex.rxjava3.kotlin.subscribeBy import javax.inject.Inject import javax.inject.Singleton @@ -21,35 +21,37 @@ import javax.inject.Singleton class UserEntryLogger @Inject constructor( private val aapsLogger: AAPSLogger, private val repository: AppRepository, - private val aapsSchedulers: AapsSchedulers + private val aapsSchedulers: AapsSchedulers, + private val dateUtil: DateUtil ) { private val compositeDisposable = CompositeDisposable() - fun log(action: Action, source: Sources, note: String? ="", vararg listvalues: ValueWithUnit?) = log(action, source, note, listvalues.toList()) + fun log(action: Action, source: Sources, note: String? = "", vararg listValues: ValueWithUnit?) = log(action, source, note, listValues.toList()) - fun log(action: Action, source: Sources, vararg listvalues: ValueWithUnit?) = log(action, source,"", listvalues.toList()) + fun log(action: Action, source: Sources, vararg listValues: ValueWithUnit?) = log(action, source, "", listValues.toList()) - fun log(action: Action, source: Sources, note: String? ="", listvalues: List = listOf()) { - val filteredValues = listvalues.toList().filterNotNull() - compositeDisposable += repository.runTransaction(UserEntryTransaction( - action = action, - source = source, - note = note ?: "", - values = filteredValues - )) + fun log(action: Action, source: Sources, note: String? = "", listValues: List = listOf()) { + val filteredValues = listValues.toList().filterNotNull() + log(listOf(UserEntryTransaction.Entry(dateUtil.now(), action, source, note ?: "", filteredValues))) + } + + fun log(entries: List) { + compositeDisposable += repository.runTransactionForResult(UserEntryTransaction(entries)) .subscribeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io) - .subscribeBy( - onError = { aapsLogger.debug("ERRORED USER ENTRY: $action $source $note $filteredValues") }, - onComplete = { aapsLogger.debug("USER ENTRY: $action $source $note $filteredValues") } + .subscribe( + { result -> result.forEach { aapsLogger.debug("USER ENTRY: ${dateUtil.dateAndTimeAndSecondsString(it.timestamp)} ${it.action} ${it.source} ${it.note} ${it.values}") } }, + { aapsLogger.debug("FAILED USER ENTRY: $it $entries") }, ) } - fun log(action: UserEntryMapper.Action, source: UserEntryMapper.Sources, note: String? ="", vararg listvalues: ValueWithUnitMapper?) = log(action.db, source.db, note, listvalues.toList().map {it?.db()}) + fun log(action: UserEntryMapper.Action, source: UserEntryMapper.Sources, note: String? = "", vararg listValues: ValueWithUnitMapper?) = + log(action.db, source.db, note, listValues.toList().map { it?.db() }) - fun log(action: UserEntryMapper.Action, source: UserEntryMapper.Sources, vararg listvalues: ValueWithUnitMapper?) = log(action.db, source.db, "", listvalues.toList().map {it?.db()}) + fun log(action: UserEntryMapper.Action, source: UserEntryMapper.Sources, vararg listValues: ValueWithUnitMapper?) = log(action.db, source.db, "", listValues.toList().map { it?.db() }) - fun log(action: UserEntryMapper.Action, source: UserEntryMapper.Sources, note: String? ="", listvalues: List = listOf()) = log(action.db, source.db, note, listvalues.map {it?.db()}) + fun log(action: UserEntryMapper.Action, source: UserEntryMapper.Sources, note: String? = "", listValues: List = listOf()) = + log(action.db, source.db, note, listValues.map { it?.db() }) } \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/receivers/DataWorkerStorage.kt b/core/src/main/java/info/nightscout/androidaps/receivers/DataWorkerStorage.kt index e790ec9f45..2e9a1ecc7a 100644 --- a/core/src/main/java/info/nightscout/androidaps/receivers/DataWorkerStorage.kt +++ b/core/src/main/java/info/nightscout/androidaps/receivers/DataWorkerStorage.kt @@ -37,6 +37,7 @@ class DataWorkerStorage @Inject constructor( return value } + @Suppress("unused") @Synchronized fun pickupString(key: Long): String? { val value = store[key] store.remove(key) @@ -63,9 +64,12 @@ class DataWorkerStorage @Inject constructor( fun enqueue(request: OneTimeWorkRequest) { WorkManager.getInstance(context) .enqueueUniqueWork(jobGroupName, ExistingWorkPolicy.APPEND_OR_REPLACE, request) - } + fun beginUniqueWork(jobName: String, request: OneTimeWorkRequest) = + WorkManager.getInstance(context) + .beginUniqueWork(jobName, ExistingWorkPolicy.APPEND_OR_REPLACE, request) + companion object { const val STORE_KEY = "storeKey" diff --git a/core/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.kt b/core/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.kt index 33b9cf70a4..4e33dec317 100644 --- a/core/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.kt +++ b/core/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.kt @@ -7,6 +7,8 @@ import android.net.Network import android.net.NetworkCapabilities import android.net.wifi.SupplicantState import android.net.wifi.WifiManager +import android.os.Handler +import android.os.HandlerThread import dagger.android.DaggerBroadcastReceiver import info.nightscout.interfaces.utils.StringUtils import info.nightscout.rx.bus.RxBus @@ -20,13 +22,14 @@ class NetworkChangeReceiver : DaggerBroadcastReceiver() { @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var receiverStatusStore: ReceiverStatusStore + private val handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) override fun onReceive(context: Context, intent: Intent) { super.onReceive(context, intent) - rxBus.send(grabNetworkStatus(context, aapsLogger)) + handler.post { rxBus.send(grabNetworkStatus(context)) } } @Suppress("DEPRECATION") - private fun grabNetworkStatus(context: Context, aapsLogger: AAPSLogger): EventNetworkChange { + private fun grabNetworkStatus(context: Context): EventNetworkChange { val event = EventNetworkChange() val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val networks: Array = cm.allNetworks diff --git a/core/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt b/core/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt index 2943970f80..9eca1f7df5 100644 --- a/core/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt +++ b/core/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt @@ -5,9 +5,7 @@ import dagger.android.AndroidInjector import dagger.android.HasAndroidInjector import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.extensions.pureProfileFromJson import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileStore @@ -16,6 +14,8 @@ import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.HardLimits +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson +import info.nightscout.interfaces.Config import info.nightscout.rx.bus.RxBus import info.nightscout.shared.sharedPreferences.SP import org.json.JSONObject diff --git a/core/src/test/java/info/nightscout/androidaps/data/ProfileTest.kt b/core/src/test/java/info/nightscout/androidaps/data/ProfileTest.kt index 8b29988c1d..dbcf168193 100644 --- a/core/src/test/java/info/nightscout/androidaps/data/ProfileTest.kt +++ b/core/src/test/java/info/nightscout/androidaps/data/ProfileTest.kt @@ -6,14 +6,14 @@ import info.nightscout.androidaps.TestBase import info.nightscout.androidaps.TestPumpPlugin import info.nightscout.androidaps.core.R import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.extensions.pureProfileFromJson import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.HardLimits +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson +import info.nightscout.interfaces.Config import info.nightscout.rx.TestAapsSchedulers import info.nightscout.rx.bus.RxBus import info.nightscout.shared.sharedPreferences.SP @@ -30,7 +30,6 @@ import java.util.Calendar /** * Created by mike on 18.03.2018. */ -@Suppress("SpellCheckingInspection") class ProfileTest : TestBase() { @Mock lateinit var activePluginProvider: ActivePlugin @@ -47,11 +46,16 @@ class ProfileTest : TestBase() { private var okProfile = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}]," + "\"sens\":[{\"time\":\"00:00\",\"value\":\"6\"},{\"time\":\"2:00\",\"value\":\"6.2\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" - private var belowLimitValidProfile = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.001\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" - private var notAlignedBasalValidProfile = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:30\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" - private var notStartingAtZeroValidProfile = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:30\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" - private var noUnitsValidProfile = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\"}" - private var wrongProfile = "{\"dia\":\"5\",\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" + private var belowLimitValidProfile = + "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.001\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" + private var notAlignedBasalValidProfile = + "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:30\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" + private var notStartingAtZeroValidProfile = + "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:30\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" + private var noUnitsValidProfile = + "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\"}" + private var wrongProfile = + "{\"dia\":\"5\",\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" //String profileStore = "{\"defaultProfile\":\"Default\",\"store\":{\"Default\":" + validProfile + "}}"; @@ -88,10 +92,12 @@ class ProfileTest : TestBase() { c[Calendar.HOUR_OF_DAY] = 2 Assert.assertEquals(111.6, p.getIsfMgdl(c.timeInMillis), 0.01) // Assert.assertEquals(110.0, p.getIsfTimeFromMidnight(2 * 60 * 60), 0.01) - Assert.assertEquals(""" + Assert.assertEquals( + """ 00:00 6,0 mmol/U 02:00 6,2 mmol/U - """.trimIndent(), p.getIsfList(rh, dateUtil).replace(".", ",")) + """.trimIndent(), p.getIsfList(rh, dateUtil).replace(".", ",") + ) Assert.assertEquals(30.0, p.getIc(c.timeInMillis), 0.01) Assert.assertEquals(30.0, p.getIcTimeFromMidnight(2 * 60 * 60), 0.01) Assert.assertEquals("00:00 30,0 g/U", p.getIcList(rh, dateUtil).replace(".", ",")) @@ -153,7 +159,8 @@ class ProfileTest : TestBase() { 00:00 6.2 mmol/U 01:00 6.0 mmol/U 03:00 6.2 mmol/U - """.trimIndent(), p.getIsfList(rh, dateUtil)) + """.trimIndent(), p.getIsfList(rh, dateUtil) + ) // Test hour alignment testPumpPlugin.pumpDescription.is30minBasalRatesCapable = false diff --git a/database/build.gradle b/database/build.gradle index 0bd96c99ff..82b79ab3f9 100644 --- a/database/build.gradle +++ b/database/build.gradle @@ -1,7 +1,10 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" diff --git a/database/src/main/java/info/nightscout/androidaps/database/entities/TherapyEvent.kt b/database/src/main/java/info/nightscout/androidaps/database/entities/TherapyEvent.kt index bea32f40d7..57361f0e33 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/entities/TherapyEvent.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/entities/TherapyEvent.kt @@ -5,12 +5,11 @@ import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.Index import androidx.room.PrimaryKey -import com.google.gson.annotations.SerializedName import info.nightscout.androidaps.database.TABLE_THERAPY_EVENTS import info.nightscout.androidaps.database.embedments.InterfaceIDs import info.nightscout.androidaps.database.interfaces.DBEntryWithTimeAndDuration import info.nightscout.androidaps.database.interfaces.TraceableDBEntry -import java.util.* +import java.util.TimeZone @Entity( tableName = TABLE_THERAPY_EVENTS, @@ -66,19 +65,16 @@ data class TherapyEvent( previous.interfaceIDs.nightscoutId == null && interfaceIDs.nightscoutId != null - enum class GlucoseUnit (val toString: String) { - MGDL (ValueWithUnit.MGDL), - MMOL (ValueWithUnit.MMOL); + enum class GlucoseUnit(val toString: String) { + MGDL(ValueWithUnit.MGDL), + MMOL(ValueWithUnit.MMOL); companion object } enum class MeterType(val text: String) { - @SerializedName("Finger") FINGER("Finger"), - @SerializedName("Sensor") SENSOR("Sensor"), - @SerializedName("Manual") MANUAL("Manual") ; @@ -91,99 +87,55 @@ data class TherapyEvent( @Suppress("unused") enum class Type(val text: String, val nsNative: Boolean = false) { - @SerializedName("Site Change") CANNULA_CHANGE("Site Change", nsNative = true), - @SerializedName("Insulin Change") INSULIN_CHANGE("Insulin Change", nsNative = true), - @SerializedName("Pump Battery Change") PUMP_BATTERY_CHANGE("Pump Battery Change", nsNative = true), - @SerializedName("Sensor Change") SENSOR_CHANGE("Sensor Change", nsNative = true), - @SerializedName("Sensor Start") SENSOR_STARTED("Sensor Start", nsNative = true), - @SerializedName("Sensor Stop") SENSOR_STOPPED("Sensor Stop", nsNative = true), - @SerializedName("BG Check") FINGER_STICK_BG_VALUE("BG Check", nsNative = true), - @SerializedName("Exercise") EXERCISE("Exercise", nsNative = true), - @SerializedName("Announcement") ANNOUNCEMENT("Announcement", nsNative = true), - @SerializedName("Question") QUESTION("Question", nsNative = true), - @SerializedName("Note") NOTE("Note", nsNative = true), - @SerializedName("OpenAPS Offline") APS_OFFLINE("OpenAPS Offline", nsNative = true), - @SerializedName("D.A.D. Alert") DAD_ALERT("D.A.D. Alert", nsNative = true), - @SerializedName("Mbg") NS_MBG("Mbg", nsNative = true), // Used but not as a Therapy Event (use constants only) - @SerializedName("Carb Correction") CARBS_CORRECTION("Carb Correction", nsNative = true), - @SerializedName("Bolus Wizard") BOLUS_WIZARD("Bolus Wizard", nsNative = true), - @SerializedName("Correction Bolus") CORRECTION_BOLUS("Correction Bolus", nsNative = true), - @SerializedName("Meal Bolus") MEAL_BOLUS("Meal Bolus", nsNative = true), - @SerializedName("Combo Bolus") COMBO_BOLUS("Combo Bolus", nsNative = true), - @SerializedName("Temporary Target") TEMPORARY_TARGET("Temporary Target", nsNative = true), - @SerializedName("Temporary Target Cancel") TEMPORARY_TARGET_CANCEL("Temporary Target Cancel", nsNative = true), - @SerializedName("Profile Switch") PROFILE_SWITCH("Profile Switch", nsNative = true), - @SerializedName("Snack Bolus") SNACK_BOLUS("Snack Bolus", nsNative = true), - @SerializedName("Temp Basal") TEMPORARY_BASAL("Temp Basal", nsNative = true), - @SerializedName("Temp Basal Start") TEMPORARY_BASAL_START("Temp Basal Start", nsNative = true), - @SerializedName("Temp Basal End") TEMPORARY_BASAL_END("Temp Basal End", nsNative = true), // Not supported by NS - @SerializedName("Tube Change") TUBE_CHANGE("Tube Change"), - @SerializedName("Falling Asleep") FALLING_ASLEEP("Falling Asleep"), - @SerializedName("Battery Empty") BATTERY_EMPTY("Battery Empty"), - @SerializedName("Reservoir Empty") RESERVOIR_EMPTY("Reservoir Empty"), - @SerializedName("Occlusion") OCCLUSION("Occlusion"), - @SerializedName("Pump Stopped") PUMP_STOPPED("Pump Stopped"), - @SerializedName("Pump Started") PUMP_STARTED("Pump Started"), - @SerializedName("Pump Paused") PUMP_PAUSED("Pump Paused"), - @SerializedName("Waking Up") WAKING_UP("Waking Up"), - @SerializedName("Sickness") SICKNESS("Sickness"), - @SerializedName("Stress") STRESS("Stress"), - @SerializedName("Pre Period") PRE_PERIOD("Pre Period"), - @SerializedName("Alcohol") ALCOHOL("Alcohol"), - @SerializedName("Cortisone") CORTISONE("Cortisone"), - @SerializedName("Feeling Low") FEELING_LOW("Feeling Low"), - @SerializedName("Feeling High") FEELING_HIGH("Feeling High"), - @SerializedName("Leaking Infusion Set") LEAKING_INFUSION_SET("Leaking Infusion Set"), // Default - @SerializedName("") NONE("") ; diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/CgmSourceTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/CgmSourceTransaction.kt index 382aed5cde..939ebbe817 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/transactions/CgmSourceTransaction.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/CgmSourceTransaction.kt @@ -7,13 +7,10 @@ import info.nightscout.androidaps.database.entities.TherapyEvent /** * Inserts data from a CGM source into the database */ -class CgmSourceTransaction( +class CgmSourceTransaction constructor( private val glucoseValues: List, private val calibrations: List, - private val sensorInsertionTime: Long?, - private val syncer: Boolean = false // caller is not native source ie. NS - // syncer is allowed create records - // update synchronization ID + private val sensorInsertionTime: Long? ) : Transaction() { override fun run(): TransactionResult { @@ -26,7 +23,8 @@ class CgmSourceTransaction( value = it.value, noise = it.noise, trendArrow = it.trendArrow, - sourceSensor = it.sourceSensor + sourceSensor = it.sourceSensor, + isValid = it.isValid ).also { gv -> gv.interfaceIDs.nightscoutId = it.nightscoutId } @@ -37,31 +35,31 @@ class CgmSourceTransaction( current?.let { existing -> glucoseValue.isValid = existing.isValid } when { // new record, create new - current == null -> { + current == null -> { database.glucoseValueDao.insertNewEntry(glucoseValue) result.inserted.add(glucoseValue) } // different record, update - !current.contentEqualsTo(glucoseValue) && !syncer -> { + !current.contentEqualsTo(glucoseValue) -> { glucoseValue.id = current.id database.glucoseValueDao.updateExistingEntry(glucoseValue) result.updated.add(glucoseValue) } // update NS id if didn't exist and now provided - current.interfaceIDs.nightscoutId == null && it.nightscoutId != null && syncer -> { - glucoseValue.id = current.id - database.glucoseValueDao.updateExistingEntry(glucoseValue) - result.updated.add(glucoseValue) + current.interfaceIDs.nightscoutId == null && it.nightscoutId != null -> { + current.interfaceIDs.nightscoutId = it.nightscoutId + database.glucoseValueDao.updateExistingEntry(current) + result.updatedNsId.add(glucoseValue) } } } calibrations.forEach { if (database.therapyEventDao.findByTimestamp(TherapyEvent.Type.FINGER_STICK_BG_VALUE, it.timestamp) == null) { val therapyEvent = TherapyEvent( - timestamp = it.timestamp, - type = TherapyEvent.Type.FINGER_STICK_BG_VALUE, - glucose = it.value, - glucoseUnit = it.glucoseUnit + timestamp = it.timestamp, + type = TherapyEvent.Type.FINGER_STICK_BG_VALUE, + glucose = it.value, + glucoseUnit = it.glucoseUnit ) database.therapyEventDao.insertNewEntry(therapyEvent) result.calibrationsInserted.add(therapyEvent) @@ -88,7 +86,8 @@ class CgmSourceTransaction( val noise: Double?, val trendArrow: GlucoseValue.TrendArrow, val nightscoutId: String? = null, - val sourceSensor: GlucoseValue.SourceSensor + val sourceSensor: GlucoseValue.SourceSensor, + val isValid: Boolean = true ) data class Calibration( @@ -101,6 +100,7 @@ class CgmSourceTransaction( val inserted = mutableListOf() val updated = mutableListOf() + val updatedNsId = mutableListOf() val calibrationsInserted = mutableListOf() val sensorInsertionsInserted = mutableListOf() diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsBolusCalculatorResultTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsBolusCalculatorResultTransaction.kt index 7ac50e32d2..bc9d4b0c07 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsBolusCalculatorResultTransaction.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsBolusCalculatorResultTransaction.kt @@ -5,41 +5,42 @@ import info.nightscout.androidaps.database.entities.BolusCalculatorResult /** * Sync the BolusCalculatorResult from NS */ -class SyncNsBolusCalculatorResultTransaction(private val bolusCalculatorResult: BolusCalculatorResult) : +class SyncNsBolusCalculatorResultTransaction(private val bolusCalculatorResults: List) : Transaction() { override fun run(): TransactionResult { val result = TransactionResult() - val current: BolusCalculatorResult? = - bolusCalculatorResult.interfaceIDs.nightscoutId?.let { - database.bolusCalculatorResultDao.findByNSId(it) + for (bolusCalculatorResult in bolusCalculatorResults) { + val current: BolusCalculatorResult? = + bolusCalculatorResult.interfaceIDs.nightscoutId?.let { + database.bolusCalculatorResultDao.findByNSId(it) + } + + if (current != null) { + // nsId exists, allow only invalidation + if (current.isValid && !bolusCalculatorResult.isValid) { + current.isValid = false + database.bolusCalculatorResultDao.updateExistingEntry(current) + result.invalidated.add(current) + } + continue } - if (current != null) { - // nsId exists, allow only invalidation - if (current.isValid && !bolusCalculatorResult.isValid) { - current.isValid = false - database.bolusCalculatorResultDao.updateExistingEntry(current) - result.invalidated.add(current) + // not known nsId + val existing = database.bolusCalculatorResultDao.findByTimestamp(bolusCalculatorResult.timestamp) + if (existing != null && existing.interfaceIDs.nightscoutId == null) { + // the same record, update nsId only + existing.interfaceIDs.nightscoutId = bolusCalculatorResult.interfaceIDs.nightscoutId + existing.isValid = bolusCalculatorResult.isValid + database.bolusCalculatorResultDao.updateExistingEntry(existing) + result.updatedNsId.add(existing) + } else { + database.bolusCalculatorResultDao.insertNewEntry(bolusCalculatorResult) + result.inserted.add(bolusCalculatorResult) } - return result - } - - // not known nsId - val existing = database.bolusCalculatorResultDao.findByTimestamp(bolusCalculatorResult.timestamp) - if (existing != null && existing.interfaceIDs.nightscoutId == null) { - // the same record, update nsId only - existing.interfaceIDs.nightscoutId = bolusCalculatorResult.interfaceIDs.nightscoutId - existing.isValid = bolusCalculatorResult.isValid - database.bolusCalculatorResultDao.updateExistingEntry(existing) - result.updatedNsId.add(existing) - } else { - database.bolusCalculatorResultDao.insertNewEntry(bolusCalculatorResult) - result.inserted.add(bolusCalculatorResult) } return result - } class TransactionResult { diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsBolusTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsBolusTransaction.kt index 9457f2d802..a7f1c8b7f4 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsBolusTransaction.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsBolusTransaction.kt @@ -5,46 +5,47 @@ import info.nightscout.androidaps.database.entities.Bolus /** * Sync the Bolus from NS */ -class SyncNsBolusTransaction(private val bolus: Bolus) : Transaction() { +class SyncNsBolusTransaction(private val boluses: List) : Transaction() { override fun run(): TransactionResult { val result = TransactionResult() - val current: Bolus? = - bolus.interfaceIDs.nightscoutId?.let { - database.bolusDao.findByNSId(it) + for (bolus in boluses) { + val current: Bolus? = + bolus.interfaceIDs.nightscoutId?.let { + database.bolusDao.findByNSId(it) + } + + if (current != null) { + // nsId exists, allow only invalidation or amount update (for drivers setting full amount upfront) + if (current.isValid && !bolus.isValid) { + current.isValid = false + database.bolusDao.updateExistingEntry(current) + result.invalidated.add(current) + } + if (current.amount != bolus.amount) { + current.amount = bolus.amount + database.bolusDao.updateExistingEntry(current) + result.updated.add(current) + } + continue } - if (current != null) { - // nsId exists, allow only invalidation or amount update (for drivers setting full amount upfront) - if (current.isValid && !bolus.isValid) { - current.isValid = false - database.bolusDao.updateExistingEntry(current) - result.invalidated.add(current) + // not known nsId + val existing = database.bolusDao.findByTimestamp(bolus.timestamp) + if (existing != null && existing.interfaceIDs.nightscoutId == null) { + // the same record, update nsId only and amount + existing.interfaceIDs.nightscoutId = bolus.interfaceIDs.nightscoutId + existing.isValid = bolus.isValid + existing.amount = bolus.amount + database.bolusDao.updateExistingEntry(existing) + result.updatedNsId.add(existing) + } else { + database.bolusDao.insertNewEntry(bolus) + result.inserted.add(bolus) } - if (current.amount != bolus.amount) { - current.amount = bolus.amount - database.bolusDao.updateExistingEntry(current) - result.updated.add(current) - } - return result - } - - // not known nsId - val existing = database.bolusDao.findByTimestamp(bolus.timestamp) - if (existing != null && existing.interfaceIDs.nightscoutId == null) { - // the same record, update nsId only and amount - existing.interfaceIDs.nightscoutId = bolus.interfaceIDs.nightscoutId - existing.isValid = bolus.isValid - existing.amount = bolus.amount - database.bolusDao.updateExistingEntry(existing) - result.updatedNsId.add(existing) - } else { - database.bolusDao.insertNewEntry(bolus) - result.inserted.add(bolus) } return result - } class TransactionResult { diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsCarbsTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsCarbsTransaction.kt index ce916a321e..5e3bb60d94 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsCarbsTransaction.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsCarbsTransaction.kt @@ -5,47 +5,48 @@ import info.nightscout.androidaps.database.entities.Carbs /** * Sync the carbs from NS */ -class SyncNsCarbsTransaction(private val carbs: Carbs) : Transaction() { +class SyncNsCarbsTransaction(private val carbs: List) : Transaction() { override fun run(): TransactionResult { val result = TransactionResult() - val current: Carbs? = - carbs.interfaceIDs.nightscoutId?.let { - database.carbsDao.findByNSId(it) + for (carb in carbs) { + val current: Carbs? = + carb.interfaceIDs.nightscoutId?.let { + database.carbsDao.findByNSId(it) + } + + if (current != null) { + // nsId exists, allow only invalidation + if (current.isValid && !carb.isValid) { + current.isValid = false + database.carbsDao.updateExistingEntry(current) + result.invalidated.add(current) + } + // and change duration + if (current.duration != carb.duration) { + current.amount = carb.amount + current.duration = carb.duration + database.carbsDao.updateExistingEntry(current) + result.updated.add(current) + } + continue } - if (current != null) { - // nsId exists, allow only invalidation - if (current.isValid && !carbs.isValid) { - current.isValid = false - database.carbsDao.updateExistingEntry(current) - result.invalidated.add(current) + // not known nsId + val existing = database.carbsDao.findByTimestamp(carb.timestamp) + if (existing != null && existing.interfaceIDs.nightscoutId == null) { + // the same record, update nsId only + existing.interfaceIDs.nightscoutId = carb.interfaceIDs.nightscoutId + existing.isValid = carb.isValid + database.carbsDao.updateExistingEntry(existing) + result.updatedNsId.add(existing) + } else { + database.carbsDao.insertNewEntry(carb) + result.inserted.add(carb) } - // and change duration - if (current.duration != carbs.duration) { - current.amount = carbs.amount - current.duration = carbs.duration - database.carbsDao.updateExistingEntry(current) - result.updated.add(current) - } - return result - } - - // not known nsId - val existing = database.carbsDao.findByTimestamp(carbs.timestamp) - if (existing != null && existing.interfaceIDs.nightscoutId == null) { - // the same record, update nsId only - existing.interfaceIDs.nightscoutId = carbs.interfaceIDs.nightscoutId - existing.isValid = carbs.isValid - database.carbsDao.updateExistingEntry(existing) - result.updatedNsId.add(existing) - } else { - database.carbsDao.insertNewEntry(carbs) - result.inserted.add(carbs) } return result - } class TransactionResult { diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsEffectiveProfileSwitchTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsEffectiveProfileSwitchTransaction.kt index 93579275e4..81d264e79a 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsEffectiveProfileSwitchTransaction.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsEffectiveProfileSwitchTransaction.kt @@ -5,37 +5,39 @@ import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch /** * Sync the EffectiveProfileSwitch from NS */ -class SyncNsEffectiveProfileSwitchTransaction(private val effectiveProfileSwitch: EffectiveProfileSwitch) : Transaction() { +class SyncNsEffectiveProfileSwitchTransaction(private val effectiveProfileSwitches: List) : Transaction() { override fun run(): TransactionResult { val result = TransactionResult() - val current: EffectiveProfileSwitch? = - effectiveProfileSwitch.interfaceIDs.nightscoutId?.let { - database.effectiveProfileSwitchDao.findByNSId(it) + for (effectiveProfileSwitch in effectiveProfileSwitches) { + val current: EffectiveProfileSwitch? = + effectiveProfileSwitch.interfaceIDs.nightscoutId?.let { + database.effectiveProfileSwitchDao.findByNSId(it) + } + + if (current != null) { + // nsId exists, allow only invalidation + if (current.isValid && !effectiveProfileSwitch.isValid) { + current.isValid = false + database.effectiveProfileSwitchDao.updateExistingEntry(current) + result.invalidated.add(current) + } + continue } - if (current != null) { - // nsId exists, allow only invalidation - if (current.isValid && !effectiveProfileSwitch.isValid) { - current.isValid = false - database.effectiveProfileSwitchDao.updateExistingEntry(current) - result.invalidated.add(current) + // not known nsId + val existing = database.effectiveProfileSwitchDao.findByTimestamp(effectiveProfileSwitch.timestamp) + if (existing != null && existing.interfaceIDs.nightscoutId == null) { + // the same record, update nsId only + existing.interfaceIDs.nightscoutId = effectiveProfileSwitch.interfaceIDs.nightscoutId + existing.isValid = effectiveProfileSwitch.isValid + database.effectiveProfileSwitchDao.updateExistingEntry(existing) + result.updatedNsId.add(existing) + } else { + database.effectiveProfileSwitchDao.insertNewEntry(effectiveProfileSwitch) + result.inserted.add(effectiveProfileSwitch) } - return result - } - - // not known nsId - val existing = database.effectiveProfileSwitchDao.findByTimestamp(effectiveProfileSwitch.timestamp) - if (existing != null && existing.interfaceIDs.nightscoutId == null) { - // the same record, update nsId only - existing.interfaceIDs.nightscoutId = effectiveProfileSwitch.interfaceIDs.nightscoutId - existing.isValid = effectiveProfileSwitch.isValid - database.effectiveProfileSwitchDao.updateExistingEntry(existing) - result.updatedNsId.add(existing) - } else { - database.effectiveProfileSwitchDao.insertNewEntry(effectiveProfileSwitch) - result.inserted.add(effectiveProfileSwitch) } return result } diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsExtendedBolusTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsExtendedBolusTransaction.kt index 1d7b7eba6e..cd8f3accd1 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsExtendedBolusTransaction.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsExtendedBolusTransaction.kt @@ -7,66 +7,68 @@ import kotlin.math.abs /** * Sync the Extended bolus from NS */ -class SyncNsExtendedBolusTransaction(private val extendedBolus: ExtendedBolus) : +class SyncNsExtendedBolusTransaction(private val extendedBoluses: List) : Transaction() { override fun run(): TransactionResult { val result = TransactionResult() - if (extendedBolus.duration != 0L) { - // not ending event - val current: ExtendedBolus? = - extendedBolus.interfaceIDs.nightscoutId?.let { - database.extendedBolusDao.findByNSId(it) + for (extendedBolus in extendedBoluses) { + if (extendedBolus.duration != 0L) { + // not ending event + val current: ExtendedBolus? = + extendedBolus.interfaceIDs.nightscoutId?.let { + database.extendedBolusDao.findByNSId(it) + } + + if (current != null) { + // nsId exists, allow only invalidation + if (current.isValid && !extendedBolus.isValid) { + current.isValid = false + database.extendedBolusDao.updateExistingEntry(current) + result.invalidated.add(current) + } + if (current.duration != extendedBolus.duration) { + current.duration = extendedBolus.duration + current.amount = extendedBolus.amount + database.extendedBolusDao.updateExistingEntry(current) + result.updatedDuration.add(current) + } + continue } - if (current != null) { - // nsId exists, allow only invalidation - if (current.isValid && !extendedBolus.isValid) { - current.isValid = false - database.extendedBolusDao.updateExistingEntry(current) - result.invalidated.add(current) + // not known nsId + val running = database.extendedBolusDao.getExtendedBolusActiveAt(extendedBolus.timestamp).blockingGet() + if (running != null && abs(running.timestamp - extendedBolus.timestamp) < 1000 && running.interfaceIDs.nightscoutId == null) { // allow missing milliseconds + // the same record, update nsId only + running.interfaceIDs.nightscoutId = extendedBolus.interfaceIDs.nightscoutId + database.extendedBolusDao.updateExistingEntry(running) + result.updatedNsId.add(running) + } else if (running != null) { + // another running record. end current and insert new + val pctRun = (extendedBolus.timestamp - running.timestamp) / running.duration.toDouble() + running.amount *= pctRun + running.end = extendedBolus.timestamp + database.extendedBolusDao.updateExistingEntry(running) + database.extendedBolusDao.insertNewEntry(extendedBolus) + result.ended.add(running) + result.inserted.add(extendedBolus) + } else { + database.extendedBolusDao.insertNewEntry(extendedBolus) + result.inserted.add(extendedBolus) } - if (current.duration != extendedBolus.duration) { - current.duration = extendedBolus.duration - current.amount = extendedBolus.amount - database.extendedBolusDao.updateExistingEntry(current) - result.updatedDuration.add(current) - } - return result - } + continue - // not known nsId - val running = database.extendedBolusDao.getExtendedBolusActiveAt(extendedBolus.timestamp).blockingGet() - if (running != null && abs(running.timestamp - extendedBolus.timestamp) < 1000 && running.interfaceIDs.nightscoutId == null) { // allow missing milliseconds - // the same record, update nsId only - running.interfaceIDs.nightscoutId = extendedBolus.interfaceIDs.nightscoutId - database.extendedBolusDao.updateExistingEntry(running) - result.updatedNsId.add(running) - } else if (running != null) { - // another running record. end current and insert new - val pctRun = (extendedBolus.timestamp - running.timestamp) / running.duration.toDouble() - running.amount *= pctRun - running.end = extendedBolus.timestamp - database.extendedBolusDao.updateExistingEntry(running) - database.extendedBolusDao.insertNewEntry(extendedBolus) - result.ended.add(running) - result.inserted.add(extendedBolus) } else { - database.extendedBolusDao.insertNewEntry(extendedBolus) - result.inserted.add(extendedBolus) - } - return result - - } else { - // ending event - val running = database.extendedBolusDao.getExtendedBolusActiveAt(extendedBolus.timestamp).blockingGet() - if (running != null) { - val pctRun = (extendedBolus.timestamp - running.timestamp) / running.duration.toDouble() - running.amount *= pctRun - running.end = extendedBolus.timestamp - database.extendedBolusDao.updateExistingEntry(running) - result.ended.add(running) + // ending event + val running = database.extendedBolusDao.getExtendedBolusActiveAt(extendedBolus.timestamp).blockingGet() + if (running != null) { + val pctRun = (extendedBolus.timestamp - running.timestamp) / running.duration.toDouble() + running.amount *= pctRun + running.end = extendedBolus.timestamp + database.extendedBolusDao.updateExistingEntry(running) + result.ended.add(running) + } } } return result diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsOfflineEventTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsOfflineEventTransaction.kt index d5fa356039..e787c591f3 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsOfflineEventTransaction.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsOfflineEventTransaction.kt @@ -7,61 +7,63 @@ import kotlin.math.abs /** * Sync the OfflineEvent from NS */ -class SyncNsOfflineEventTransaction(private val offlineEvent: OfflineEvent) : +class SyncNsOfflineEventTransaction(private val offlineEvents: List) : Transaction() { override fun run(): TransactionResult { val result = TransactionResult() - if (offlineEvent.duration != 0L) { - // not ending event - val current: OfflineEvent? = - offlineEvent.interfaceIDs.nightscoutId?.let { - database.offlineEventDao.findByNSId(it) + for (offlineEvent in offlineEvents) { + if (offlineEvent.duration != 0L) { + // not ending event + val current: OfflineEvent? = + offlineEvent.interfaceIDs.nightscoutId?.let { + database.offlineEventDao.findByNSId(it) + } + + if (current != null) { + // nsId exists, allow only invalidation + if (current.isValid && !offlineEvent.isValid) { + current.isValid = false + database.offlineEventDao.updateExistingEntry(current) + result.invalidated.add(current) + } + if (current.duration != offlineEvent.duration) { + current.duration = offlineEvent.duration + database.offlineEventDao.updateExistingEntry(current) + result.updatedDuration.add(current) + } + continue } - if (current != null) { - // nsId exists, allow only invalidation - if (current.isValid && !offlineEvent.isValid) { - current.isValid = false - database.offlineEventDao.updateExistingEntry(current) - result.invalidated.add(current) + // not known nsId + val running = database.offlineEventDao.getOfflineEventActiveAt(offlineEvent.timestamp).blockingGet() + if (running != null && abs(running.timestamp - offlineEvent.timestamp) < 1000 && running.interfaceIDs.nightscoutId == null) { // allow missing milliseconds + // the same record, update nsId only + running.interfaceIDs.nightscoutId = offlineEvent.interfaceIDs.nightscoutId + database.offlineEventDao.updateExistingEntry(running) + result.updatedNsId.add(running) + } else if (running != null) { + // another running record. end current and insert new + running.end = offlineEvent.timestamp + database.offlineEventDao.updateExistingEntry(running) + database.offlineEventDao.insertNewEntry(offlineEvent) + result.ended.add(running) + result.inserted.add(offlineEvent) + } else { + database.offlineEventDao.insertNewEntry(offlineEvent) + result.inserted.add(offlineEvent) } - if (current.duration != offlineEvent.duration) { - current.duration = offlineEvent.duration - database.offlineEventDao.updateExistingEntry(current) - result.updatedDuration.add(current) - } - return result - } + continue - // not known nsId - val running = database.offlineEventDao.getOfflineEventActiveAt(offlineEvent.timestamp).blockingGet() - if (running != null && abs(running.timestamp - offlineEvent.timestamp) < 1000 && running.interfaceIDs.nightscoutId == null) { // allow missing milliseconds - // the same record, update nsId only - running.interfaceIDs.nightscoutId = offlineEvent.interfaceIDs.nightscoutId - database.offlineEventDao.updateExistingEntry(running) - result.updatedNsId.add(running) - } else if (running != null) { - // another running record. end current and insert new - running.end = offlineEvent.timestamp - database.offlineEventDao.updateExistingEntry(running) - database.offlineEventDao.insertNewEntry(offlineEvent) - result.ended.add(running) - result.inserted.add(offlineEvent) } else { - database.offlineEventDao.insertNewEntry(offlineEvent) - result.inserted.add(offlineEvent) - } - return result - - } else { - // ending event - val running = database.offlineEventDao.getOfflineEventActiveAt(offlineEvent.timestamp).blockingGet() - if (running != null) { - running.end = offlineEvent.timestamp - database.offlineEventDao.updateExistingEntry(running) - result.ended.add(running) + // ending event + val running = database.offlineEventDao.getOfflineEventActiveAt(offlineEvent.timestamp).blockingGet() + if (running != null) { + running.end = offlineEvent.timestamp + database.offlineEventDao.updateExistingEntry(running) + result.ended.add(running) + } } } return result diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsProfileSwitchTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsProfileSwitchTransaction.kt index e99d79a334..e11f5ebad0 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsProfileSwitchTransaction.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsProfileSwitchTransaction.kt @@ -5,37 +5,39 @@ import info.nightscout.androidaps.database.entities.ProfileSwitch /** * Sync the ProfileSwitch from NS */ -class SyncNsProfileSwitchTransaction(private val profileSwitch: ProfileSwitch) : Transaction() { +class SyncNsProfileSwitchTransaction(private val profileSwitches: List) : Transaction() { override fun run(): TransactionResult { val result = TransactionResult() - val current: ProfileSwitch? = - profileSwitch.interfaceIDs.nightscoutId?.let { - database.profileSwitchDao.findByNSId(it) + for (profileSwitch in profileSwitches) { + val current: ProfileSwitch? = + profileSwitch.interfaceIDs.nightscoutId?.let { + database.profileSwitchDao.findByNSId(it) + } + + if (current != null) { + // nsId exists, allow only invalidation + if (current.isValid && !profileSwitch.isValid) { + current.isValid = false + database.profileSwitchDao.updateExistingEntry(current) + result.invalidated.add(current) + } + continue } - if (current != null) { - // nsId exists, allow only invalidation - if (current.isValid && !profileSwitch.isValid) { - current.isValid = false - database.profileSwitchDao.updateExistingEntry(current) - result.invalidated.add(current) + // not known nsId + val existing = database.profileSwitchDao.findByTimestamp(profileSwitch.timestamp) + if (existing != null && existing.interfaceIDs.nightscoutId == null) { + // the same record, update nsId only + existing.interfaceIDs.nightscoutId = profileSwitch.interfaceIDs.nightscoutId + existing.isValid = profileSwitch.isValid + database.profileSwitchDao.updateExistingEntry(existing) + result.updatedNsId.add(existing) + } else { + database.profileSwitchDao.insertNewEntry(profileSwitch) + result.inserted.add(profileSwitch) } - return result - } - - // not known nsId - val existing = database.profileSwitchDao.findByTimestamp(profileSwitch.timestamp) - if (existing != null && existing.interfaceIDs.nightscoutId == null) { - // the same record, update nsId only - existing.interfaceIDs.nightscoutId = profileSwitch.interfaceIDs.nightscoutId - existing.isValid = profileSwitch.isValid - database.profileSwitchDao.updateExistingEntry(existing) - result.updatedNsId.add(existing) - } else { - database.profileSwitchDao.insertNewEntry(profileSwitch) - result.inserted.add(profileSwitch) } return result } diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsTemporaryBasalTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsTemporaryBasalTransaction.kt index 8a4931617a..1789102029 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsTemporaryBasalTransaction.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsTemporaryBasalTransaction.kt @@ -7,54 +7,54 @@ import kotlin.math.abs /** * Sync the Temporary Basal from NS */ -class SyncNsTemporaryBasalTransaction( - private val temporaryBasal: TemporaryBasal -) : Transaction() { +class SyncNsTemporaryBasalTransaction(private val temporaryBasals: List) : Transaction() { override fun run(): TransactionResult { val result = TransactionResult() - if (temporaryBasal.duration != 0L) { - // not ending event - val current: TemporaryBasal? = - temporaryBasal.interfaceIDs.nightscoutId?.let { - database.temporaryBasalDao.findByNSId(it) + for (temporaryBasal in temporaryBasals) { + if (temporaryBasal.duration != 0L) { + // not ending event + val current = temporaryBasal.interfaceIDs.nightscoutId?.let { + database.temporaryBasalDao.findByNSId(it) ?: temporaryBasal.interfaceIDs.pumpId?.let { + database.temporaryBasalDao.findByPumpIds(temporaryBasal.interfaceIDs.pumpId!!, temporaryBasal.interfaceIDs.pumpType!!, temporaryBasal.interfaceIDs.pumpSerial!!) + } } - if (current != null) { - // nsId exists, allow only invalidation - if (current.isValid && !temporaryBasal.isValid) { - current.isValid = false - database.temporaryBasalDao.updateExistingEntry(current) - result.invalidated.add(current) + if (current != null) { + // nsId exists, allow only invalidation + if (current.isValid && !temporaryBasal.isValid) { + current.isValid = false + database.temporaryBasalDao.updateExistingEntry(current) + result.invalidated.add(current) + } + if (current.duration != temporaryBasal.duration) { + current.duration = temporaryBasal.duration + database.temporaryBasalDao.updateExistingEntry(current) + result.updatedDuration.add(current) + } + continue } - if (current.duration != temporaryBasal.duration) { - current.duration = temporaryBasal.duration - database.temporaryBasalDao.updateExistingEntry(current) - result.updatedDuration.add(current) - } - return result - } - // not known nsId - val running = database.temporaryBasalDao.getTemporaryBasalActiveAt(temporaryBasal.timestamp).blockingGet() - if (running != null && abs(running.timestamp - temporaryBasal.timestamp) < 1000 && running.interfaceIDs.nightscoutId == null) { // allow missing milliseconds - // the same record, update nsId only - running.interfaceIDs.nightscoutId = temporaryBasal.interfaceIDs.nightscoutId - database.temporaryBasalDao.updateExistingEntry(running) - result.updatedNsId.add(running) - } else if (running != null) { - // another running record. end current and insert new - running.end = temporaryBasal.timestamp - database.temporaryBasalDao.updateExistingEntry(running) - database.temporaryBasalDao.insertNewEntry(temporaryBasal) - result.ended.add(running) - result.inserted.add(temporaryBasal) - } else { - database.temporaryBasalDao.insertNewEntry(temporaryBasal) - result.inserted.add(temporaryBasal) + // not known nsId + val running = database.temporaryBasalDao.getTemporaryBasalActiveAt(temporaryBasal.timestamp).blockingGet() + if (running != null && abs(running.timestamp - temporaryBasal.timestamp) < 1000 && running.interfaceIDs.nightscoutId == null) { // allow missing milliseconds + // the same record, update nsId only + running.interfaceIDs.nightscoutId = temporaryBasal.interfaceIDs.nightscoutId + database.temporaryBasalDao.updateExistingEntry(running) + result.updatedNsId.add(running) + } else if (running != null) { + // another running record. end current and insert new + running.end = temporaryBasal.timestamp + database.temporaryBasalDao.updateExistingEntry(running) + database.temporaryBasalDao.insertNewEntry(temporaryBasal) + result.ended.add(running) + result.inserted.add(temporaryBasal) + } else { + database.temporaryBasalDao.insertNewEntry(temporaryBasal) + result.inserted.add(temporaryBasal) + } } - return result } return result } diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsTemporaryTargetTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsTemporaryTargetTransaction.kt index a3eff6d1e6..d3fcb29cf1 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsTemporaryTargetTransaction.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsTemporaryTargetTransaction.kt @@ -7,61 +7,62 @@ import kotlin.math.abs /** * Sync the TemporaryTarget from NS */ -class SyncNsTemporaryTargetTransaction(private val temporaryTarget: TemporaryTarget) : +class SyncNsTemporaryTargetTransaction(private val temporaryTargets: List) : Transaction() { override fun run(): TransactionResult { val result = TransactionResult() - if (temporaryTarget.duration != 0L) { - // not ending event - val current: TemporaryTarget? = - temporaryTarget.interfaceIDs.nightscoutId?.let { - database.temporaryTargetDao.findByNSId(it) + for (temporaryTarget in temporaryTargets) { + if (temporaryTarget.duration != 0L) { + // not ending event + val current: TemporaryTarget? = + temporaryTarget.interfaceIDs.nightscoutId?.let { + database.temporaryTargetDao.findByNSId(it) + } + + if (current != null) { + // nsId exists, allow only invalidation + if (current.isValid && !temporaryTarget.isValid) { + current.isValid = false + database.temporaryTargetDao.updateExistingEntry(current) + result.invalidated.add(current) + } + if (current.duration != temporaryTarget.duration) { + current.duration = temporaryTarget.duration + database.temporaryTargetDao.updateExistingEntry(current) + result.updatedDuration.add(current) + } + continue } - if (current != null) { - // nsId exists, allow only invalidation - if (current.isValid && !temporaryTarget.isValid) { - current.isValid = false - database.temporaryTargetDao.updateExistingEntry(current) - result.invalidated.add(current) + // not known nsId + val running = database.temporaryTargetDao.getTemporaryTargetActiveAt(temporaryTarget.timestamp).blockingGet() + if (running != null && abs(running.timestamp - temporaryTarget.timestamp) < 1000 && running.interfaceIDs.nightscoutId == null) { // allow missing milliseconds + // the same record, update nsId only + running.interfaceIDs.nightscoutId = temporaryTarget.interfaceIDs.nightscoutId + database.temporaryTargetDao.updateExistingEntry(running) + result.updatedNsId.add(running) + } else if (running != null) { + // another running record. end current and insert new + running.end = temporaryTarget.timestamp + database.temporaryTargetDao.updateExistingEntry(running) + database.temporaryTargetDao.insertNewEntry(temporaryTarget) + result.ended.add(running) + result.inserted.add(temporaryTarget) + } else { + database.temporaryTargetDao.insertNewEntry(temporaryTarget) + result.inserted.add(temporaryTarget) } - if (current.duration != temporaryTarget.duration) { - current.duration = temporaryTarget.duration - database.temporaryTargetDao.updateExistingEntry(current) - result.updatedDuration.add(current) - } - return result - } - - // not known nsId - val running = database.temporaryTargetDao.getTemporaryTargetActiveAt(temporaryTarget.timestamp).blockingGet() - if (running != null && abs(running.timestamp - temporaryTarget.timestamp) < 1000 && running.interfaceIDs.nightscoutId == null) { // allow missing milliseconds - // the same record, update nsId only - running.interfaceIDs.nightscoutId = temporaryTarget.interfaceIDs.nightscoutId - database.temporaryTargetDao.updateExistingEntry(running) - result.updatedNsId.add(running) - } else if (running != null) { - // another running record. end current and insert new - running.end = temporaryTarget.timestamp - database.temporaryTargetDao.updateExistingEntry(running) - database.temporaryTargetDao.insertNewEntry(temporaryTarget) - result.ended.add(running) - result.inserted.add(temporaryTarget) + continue } else { - database.temporaryTargetDao.insertNewEntry(temporaryTarget) - result.inserted.add(temporaryTarget) - } - return result - - } else { - // ending event - val running = database.temporaryTargetDao.getTemporaryTargetActiveAt(temporaryTarget.timestamp).blockingGet() - if (running != null) { - running.end = temporaryTarget.timestamp - database.temporaryTargetDao.updateExistingEntry(running) - result.ended.add(running) + // ending event + val running = database.temporaryTargetDao.getTemporaryTargetActiveAt(temporaryTarget.timestamp).blockingGet() + if (running != null) { + running.end = temporaryTarget.timestamp + database.temporaryTargetDao.updateExistingEntry(running) + result.ended.add(running) + } } } return result diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsTherapyEventTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsTherapyEventTransaction.kt index 5ed73cf9d8..f0fa5104c1 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsTherapyEventTransaction.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsTherapyEventTransaction.kt @@ -5,46 +5,47 @@ import info.nightscout.androidaps.database.entities.TherapyEvent /** * Sync the TherapyEvents from NS */ -class SyncNsTherapyEventTransaction(private val therapyEvent: TherapyEvent) : +class SyncNsTherapyEventTransaction(private val therapyEvents: List) : Transaction() { override fun run(): TransactionResult { val result = TransactionResult() - val current: TherapyEvent? = - therapyEvent.interfaceIDs.nightscoutId?.let { - database.therapyEventDao.findByNSId(it) + for (therapyEvent in therapyEvents) { + val current: TherapyEvent? = + therapyEvent.interfaceIDs.nightscoutId?.let { + database.therapyEventDao.findByNSId(it) + } + + if (current != null) { + // nsId exists, allow only invalidation + if (current.isValid && !therapyEvent.isValid) { + current.isValid = false + database.therapyEventDao.updateExistingEntry(current) + result.invalidated.add(current) + } + if (current.duration != therapyEvent.duration) { + current.duration = therapyEvent.duration + database.therapyEventDao.updateExistingEntry(current) + result.updatedDuration.add(current) + } + continue } - if (current != null) { - // nsId exists, allow only invalidation - if (current.isValid && !therapyEvent.isValid) { - current.isValid = false - database.therapyEventDao.updateExistingEntry(current) - result.invalidated.add(current) + // not known nsId + val existing = database.therapyEventDao.findByTimestamp(therapyEvent.type, therapyEvent.timestamp) + if (existing != null && existing.interfaceIDs.nightscoutId == null) { + // the same record, update nsId only + existing.interfaceIDs.nightscoutId = therapyEvent.interfaceIDs.nightscoutId + existing.isValid = therapyEvent.isValid + database.therapyEventDao.updateExistingEntry(existing) + result.updatedNsId.add(existing) + } else { + database.therapyEventDao.insertNewEntry(therapyEvent) + result.inserted.add(therapyEvent) } - if (current.duration != therapyEvent.duration) { - current.duration = therapyEvent.duration - database.therapyEventDao.updateExistingEntry(current) - result.updatedDuration.add(current) - } - return result - } - - // not known nsId - val existing = database.therapyEventDao.findByTimestamp(therapyEvent.type, therapyEvent.timestamp) - if (existing != null && existing.interfaceIDs.nightscoutId == null) { - // the same record, update nsId only - existing.interfaceIDs.nightscoutId = therapyEvent.interfaceIDs.nightscoutId - existing.isValid = therapyEvent.isValid - database.therapyEventDao.updateExistingEntry(existing) - result.updatedNsId.add(existing) - } else { - database.therapyEventDao.insertNewEntry(therapyEvent) - result.inserted.add(therapyEvent) } return result - } class TransactionResult { diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/UserEntryTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/UserEntryTransaction.kt index 714661fc36..6443041f2c 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/transactions/UserEntryTransaction.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/UserEntryTransaction.kt @@ -5,20 +5,28 @@ import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.ValueWithUnit -class UserEntryTransaction( - val action: Action, - val source: Sources, - val note: String, - val values: List = listOf() -) : Transaction() { +class UserEntryTransaction(private val entries: List) : Transaction>() { - override fun run() { - database.userEntryDao.insert(UserEntry( - timestamp = System.currentTimeMillis(), - action = action, - source = source, - note = note, - values = values - )) + data class Entry( + val timestamp: Long, + val action: Action, + val source: Sources, + val note: String, + val values: List = listOf() + ) + + override fun run(): List { + + for (entry in entries) + database.userEntryDao.insert( + UserEntry( + timestamp = entry.timestamp, + action = entry.action, + source = entry.source, + note = entry.note, + values = entry.values + ) + ) + return entries } } \ No newline at end of file diff --git a/graphview/build.gradle b/graphview/build.gradle index d4855aaefe..a7e69d8d3e 100644 --- a/graphview/build.gradle +++ b/graphview/build.gradle @@ -1,6 +1,8 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + } apply from: "${project.rootDir}/core/android_dependencies.gradle" diff --git a/implementation/build.gradle b/implementation/build.gradle index fd1b013ab8..c7648b67ef 100644 --- a/implementation/build.gradle +++ b/implementation/build.gradle @@ -1,12 +1,15 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" android { namespace 'info.nightscout.implementation' diff --git a/implementation/src/main/java/info/nightscout/implementation/queue/CommandQueueImplementation.kt b/implementation/src/main/java/info/nightscout/implementation/queue/CommandQueueImplementation.kt index 69cb50a4bd..9131218c6c 100644 --- a/implementation/src/main/java/info/nightscout/implementation/queue/CommandQueueImplementation.kt +++ b/implementation/src/main/java/info/nightscout/implementation/queue/CommandQueueImplementation.kt @@ -9,7 +9,6 @@ import dagger.android.HasAndroidInjector import info.nightscout.androidaps.annotations.OpenForTesting import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.ProfileSealed -import info.nightscout.interfaces.data.PumpEnactResult import info.nightscout.androidaps.data.PumpEnactResultImpl import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper @@ -17,10 +16,7 @@ import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch import info.nightscout.androidaps.database.entities.ProfileSwitch import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.dialogs.BolusProgressDialog -import info.nightscout.androidaps.extensions.getCustomizedName import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.ActivityNames -import info.nightscout.interfaces.AndroidPermission import info.nightscout.androidaps.interfaces.CommandQueue import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraints @@ -31,14 +27,11 @@ import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification -import info.nightscout.interfaces.notifications.Notification -import info.nightscout.interfaces.queue.Callback import info.nightscout.androidaps.queue.commands.Command import info.nightscout.androidaps.queue.commands.Command.CommandType -import info.nightscout.interfaces.queue.CustomCommand import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.interfaces.utils.HtmlHelper +import info.nightscout.androidaps.utils.extensions.getCustomizedName import info.nightscout.implementation.R import info.nightscout.implementation.queue.commands.CommandBolus import info.nightscout.implementation.queue.commands.CommandCancelExtendedBolus @@ -57,8 +50,15 @@ import info.nightscout.implementation.queue.commands.CommandStartPump import info.nightscout.implementation.queue.commands.CommandStopPump import info.nightscout.implementation.queue.commands.CommandTempBasalAbsolute import info.nightscout.implementation.queue.commands.CommandTempBasalPercent +import info.nightscout.interfaces.ActivityNames +import info.nightscout.interfaces.AndroidPermission import info.nightscout.interfaces.BuildHelper import info.nightscout.interfaces.Config +import info.nightscout.interfaces.data.PumpEnactResult +import info.nightscout.interfaces.notifications.Notification +import info.nightscout.interfaces.queue.Callback +import info.nightscout.interfaces.queue.CustomCommand +import info.nightscout.interfaces.utils.HtmlHelper import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventMobileToWear diff --git a/implementation/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt b/implementation/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt index 78bd435dc5..c8c0e6e0e3 100644 --- a/implementation/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt +++ b/implementation/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt @@ -6,15 +6,15 @@ import dagger.android.HasAndroidInjector import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.database.embedments.InsulinConfiguration import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch -import info.nightscout.androidaps.extensions.pureProfileFromJson import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileStore import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson +import info.nightscout.interfaces.Config import info.nightscout.rx.bus.RxBus import org.json.JSONObject import org.junit.Before diff --git a/insight/build.gradle b/insight/build.gradle index ff816bf0d1..55feaf304e 100644 --- a/insight/build.gradle +++ b/insight/build.gradle @@ -1,11 +1,14 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" diff --git a/interfaces/build.gradle b/interfaces/build.gradle index 09d5a2a9d5..e94dcc4ac3 100644 --- a/interfaces/build.gradle +++ b/interfaces/build.gradle @@ -1,10 +1,12 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'kotlin-parcelize' -apply plugin: 'com.hiya.jacoco-android' -apply plugin: 'kotlinx-serialization' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'kotlin-parcelize' + id 'kotlinx-serialization' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" diff --git a/interfaces/src/main/java/info/nightscout/interfaces/Constants.kt b/interfaces/src/main/java/info/nightscout/interfaces/Constants.kt index 9de3afd8cb..d72293006b 100644 --- a/interfaces/src/main/java/info/nightscout/interfaces/Constants.kt +++ b/interfaces/src/main/java/info/nightscout/interfaces/Constants.kt @@ -46,7 +46,7 @@ object Constants { const val MAX_TT_MMOL = 10.0 //NSClientInternal - const val MAX_LOG_LINES = 30 + const val MAX_LOG_LINES = 90 //Screen: Threshold for width/height to go into small width/height layout const val SMALL_WIDTH = 320 diff --git a/interfaces/src/main/java/info/nightscout/interfaces/PluginType.kt b/interfaces/src/main/java/info/nightscout/interfaces/PluginType.kt index 648b6bac9b..f57b33b863 100644 --- a/interfaces/src/main/java/info/nightscout/interfaces/PluginType.kt +++ b/interfaces/src/main/java/info/nightscout/interfaces/PluginType.kt @@ -6,5 +6,5 @@ package info.nightscout.interfaces * set by [info.nightscout.androidaps.interfaces.PluginDescription.mainType] */ enum class PluginType { - GENERAL, SENSITIVITY, PROFILE, APS, PUMP, CONSTRAINTS, LOOP, BGSOURCE, INSULIN + GENERAL, SENSITIVITY, PROFILE, APS, PUMP, CONSTRAINTS, LOOP, BGSOURCE, INSULIN, SYNC } \ No newline at end of file diff --git a/ns-sdk/.gitignore b/ns-sdk/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/ns-sdk/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/ns-sdk/build.gradle b/ns-sdk/build.gradle new file mode 100644 index 0000000000..d8c4d43bd3 --- /dev/null +++ b/ns-sdk/build.gradle @@ -0,0 +1,38 @@ +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlinx-serialization' +} + +apply from: "${project.rootDir}/core/android_dependencies.gradle" +apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/test_dependencies.gradle" + +android { + namespace 'info.nightscout.sdk' +} + +dependencies { + api "com.squareup.retrofit2:retrofit:$retrofit2_version" + api "com.squareup.retrofit2:adapter-rxjava3:$retrofit2_version" + api "com.squareup.retrofit2:converter-gson:$retrofit2_version" + api "com.squareup.okhttp3:okhttp:$okhttp3_version" + api "com.squareup.okhttp3:logging-interceptor:$okhttp3_version" + + api "com.google.code.gson:gson:$gson_version" + + api "net.danlew:android.joda:$joda_version" + + api "io.reactivex.rxjava3:rxjava:$rxjava_version" + api "io.reactivex.rxjava3:rxandroid:$rxandroid_version" + api "io.reactivex.rxjava3:rxkotlin:$rxkotlin_version" + + api "androidx.core:core-ktx:$core_version" + api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" + api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" + api "org.jetbrains.kotlinx:kotlinx-coroutines-rx3:$coroutines_version" + + api "org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization_version" +} diff --git a/ns-sdk/proguard-rules.pro b/ns-sdk/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/ns-sdk/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/ns-sdk/src/main/AndroidManifest.xml b/ns-sdk/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a8800291f3 --- /dev/null +++ b/ns-sdk/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidCallbackClientImpl.kt b/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidCallbackClientImpl.kt new file mode 100644 index 0000000000..1da0a47e9b --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidCallbackClientImpl.kt @@ -0,0 +1,27 @@ +package info.nightscout.sdk + +import info.nightscout.sdk.interfaces.NSAndroidCallbackClient +import info.nightscout.sdk.interfaces.NSAndroidClient +import info.nightscout.sdk.localmodel.Status +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch + +class NSAndroidCallbackClientImpl(private val client: NSAndroidClient) : + NSAndroidCallbackClient { + + private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) + + @Suppress("TooGenericExceptionCaught") + override fun getStatus(callback: NSAndroidCallbackClient.NSCallback): NSAndroidCallbackClient.NSCancellable = + NSAndroidCallbackClient.NSJobCancellable( + scope.launch { + try { + callback.onSuccess(client.getStatus()) + } catch (e: Exception) { + callback.onFailure(e) + } + } + ) +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidClientImpl.kt b/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidClientImpl.kt new file mode 100644 index 0000000000..4a5b3ffa10 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidClientImpl.kt @@ -0,0 +1,175 @@ +package info.nightscout.sdk + +import android.content.Context +import info.nightscout.sdk.exceptions.DateHeaderOutOfToleranceException +import info.nightscout.sdk.exceptions.InvalidAccessTokenException +import info.nightscout.sdk.exceptions.TodoNightscoutException +import info.nightscout.sdk.interfaces.NSAndroidClient +import info.nightscout.sdk.localmodel.Status +import info.nightscout.sdk.localmodel.entry.NSSgvV3 +import info.nightscout.sdk.localmodel.treatment.NSTreatment +import info.nightscout.sdk.mapper.toLocal +import info.nightscout.sdk.mapper.toSgv +import info.nightscout.sdk.mapper.toTreatment +import info.nightscout.sdk.networking.NetworkStackBuilder +import info.nightscout.sdk.remotemodel.LastModified +import info.nightscout.sdk.remotemodel.RemoteDeviceStatus +import info.nightscout.sdk.remotemodel.RemoteEntry +import info.nightscout.sdk.remotemodel.RemoteTreatment +import info.nightscout.sdk.utils.retry +import info.nightscout.sdk.utils.toNotNull +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +/** + * + * This client uses suspend functions and therefore is only visible in Kotlin (@JvmSynthetic). + * An RxJava version can be found here [NSAndroidRxClientImpl] + * + * @param baseUrl the baseURL of the NightScout Instance + * @param accessToken the access token of a role found in the admin panel of the NightScout instance + * @param logging if set to true, all network communication will be logged to logcat + * @param dispatcher the coroutine dispatcher used for network calls. + * Per default all network calls will be done on the IO thread pool. Change for Unit-Tests + * @param context the application context. + * + * Todo: retry parameters (maxRetries, backoffFactor)? + * + * Todo: functions to modify baseUrl and accessToken? + * (not necessarily needed but might come handy if Client is provided by a DI framework like dagger) + * + * Todo: internal methods are still visible in Java bytecode -> tag @JvmSynthetic + * + * TODO: add message to Exceptions? wrap them? + * */ + +class NSAndroidClientImpl( + baseUrl: String, + accessToken: String, + context: Context, + logging: Boolean, + private val dispatcher: CoroutineDispatcher = Dispatchers.IO +) : NSAndroidClient { + + internal val api = NetworkStackBuilder.getApi( + baseUrl = baseUrl, + context = context, + accessToken = accessToken, + logging = logging + ) + + /* + * TODO: how should our result look like? + * + * Option A: + * Directly hat the user asked for or an Exception. We can have our own Exceptions + * -> re-throw to Exceptions with meaning + * -> usually not that liked in Java with checked Exceptions and Rx + * + * + * + * Option B: + * A Wrapper - sealed class that has success and error sub types. + * Typical for Rx. + * + * */ + + // TODO: we need a minimum NightscoutVersion for APIv3. Add to documentation + override suspend fun getVersion(): String = callWrapper(dispatcher) { + api.statusSimple().result!!.version + } + + override suspend fun getStatus(): Status = callWrapper(dispatcher) { + api.statusSimple().result!!.toLocal() + } + + // TODO: return something better than a String + // TODO: parameters like count? + // TODO: updated after timestamp + override suspend fun getEntries(): String = callWrapper(dispatcher) { + api.getEntries().toString() + } + + override suspend fun getLastModified(): LastModified = callWrapper(dispatcher) { + + val response = api.lastModified() + if (response.isSuccessful) { + return@callWrapper response.body()?.result ?: throw TodoNightscoutException() + } else { + throw TodoNightscoutException() // TODO: react to response errors (offline, ...) + } + } + + // TODO: parameters like count? + override suspend fun getSgvs(): List = callWrapper(dispatcher) { + + val response = api.getSgvs() + if (response.isSuccessful) { + return@callWrapper response.body()?.result?.map(RemoteEntry::toSgv).toNotNull() + } else { + throw TodoNightscoutException() // TODO: react to response errors (offline, ...) + } + } + + override suspend fun getSgvsModifiedSince(from: Long): List = callWrapper(dispatcher) { + + val response = api.getSgvsModifiedSince(from) + if (response.isSuccessful) { + return@callWrapper response.body()?.result?.map(RemoteEntry::toSgv).toNotNull() + } else { + throw TodoNightscoutException() // TODO: react to response errors (offline, ...) + } + } + + override suspend fun getSgvsNewerThan(from: Long, limit: Long): List = callWrapper(dispatcher) { + + val response = api.getSgvsNewerThan(from, limit) + if (response.isSuccessful) { + return@callWrapper response.body()?.result?.map(RemoteEntry::toSgv).toNotNull() + } else { + throw TodoNightscoutException() // TODO: react to response errors (offline, ...) + } + } + + override suspend fun getTreatmentsModifiedSince(from: Long, limit: Long): List = callWrapper(dispatcher) { + + val response = api.getTreatmentsModifiedSince(from, limit) + if (response.isSuccessful) { + return@callWrapper response.body()?.result?.map(RemoteTreatment::toTreatment).toNotNull() + } else { + throw TodoNightscoutException() // TODO: react to response errors (offline, ...) + } + } + + override suspend fun getDeviceStatusModifiedSince(from: Long): List = callWrapper(dispatcher) { + + val response = api.getDeviceStatusModifiedSince(from) + if (response.isSuccessful) { + return@callWrapper response.body()?.result.toNotNull() + } else { + throw TodoNightscoutException() // TODO: react to response errors (offline, ...) + } + } + + private suspend fun callWrapper(dispatcher: CoroutineDispatcher, block: suspend () -> T): T = + withContext(dispatcher) { + retry( + numberOfRetries = RETRIES, + delayBetweenRetries = RETRY_DELAY, + excludedExceptions = listOf( + InvalidAccessTokenException::class, + DateHeaderOutOfToleranceException::class + ) + ) { + block.invoke() + } + } + + companion object { + + // TODO: Parameters? + private const val RETRIES = 3 + private const val RETRY_DELAY = 100L + } +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidRxClientImpl.kt b/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidRxClientImpl.kt new file mode 100644 index 0000000000..93b11ca0ce --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidRxClientImpl.kt @@ -0,0 +1,23 @@ +package info.nightscout.sdk + +import info.nightscout.sdk.interfaces.NSAndroidClient +import info.nightscout.sdk.interfaces.NSAndroidRxClient +import info.nightscout.sdk.localmodel.Status +import info.nightscout.sdk.localmodel.entry.NSSgvV3 +import info.nightscout.sdk.localmodel.treatment.NSTreatment +import info.nightscout.sdk.remotemodel.LastModified +import info.nightscout.sdk.remotemodel.RemoteDeviceStatus +import io.reactivex.rxjava3.core.Single +import kotlinx.coroutines.rx3.rxSingle + +class NSAndroidRxClientImpl(private val client: NSAndroidClient) : NSAndroidRxClient { + + override fun getVersion(): Single = rxSingle { client.getVersion() } + override fun getStatus(): Single = rxSingle { client.getStatus() } + override fun getLastModified(): Single = rxSingle { client.getLastModified() } + override fun getSgvsModifiedSince(from: Long): Single> = rxSingle { client.getSgvsModifiedSince(from) } + override fun getTreatmentsModifiedSince(from: Long, limit: Long): Single> = + rxSingle { client.getTreatmentsModifiedSince(from, limit) } + override fun getDeviceStatusModifiedSince(from: Long): Single> = + rxSingle { client.getDeviceStatusModifiedSince(from) } +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/exceptions/DateHeaderOutOfToleranceException.kt b/ns-sdk/src/main/java/info/nightscout/sdk/exceptions/DateHeaderOutOfToleranceException.kt new file mode 100644 index 0000000000..2cfcdc1bf6 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/exceptions/DateHeaderOutOfToleranceException.kt @@ -0,0 +1,9 @@ +package info.nightscout.sdk.exceptions + +/** + * Will be thrown if the server responds with 401 UNAUTHORIZED due to the Date Header being off + * more than one hour. + * In practice this will happen if the server time and the phone time are off. + * + */ +class DateHeaderOutOfToleranceException : NightscoutException() diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/exceptions/InvalidAccessTokenException.kt b/ns-sdk/src/main/java/info/nightscout/sdk/exceptions/InvalidAccessTokenException.kt new file mode 100644 index 0000000000..6ae3735741 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/exceptions/InvalidAccessTokenException.kt @@ -0,0 +1,3 @@ +package info.nightscout.sdk.exceptions + +class InvalidAccessTokenException : NightscoutException() diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/exceptions/NightscoutException.kt b/ns-sdk/src/main/java/info/nightscout/sdk/exceptions/NightscoutException.kt new file mode 100644 index 0000000000..ff7817e277 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/exceptions/NightscoutException.kt @@ -0,0 +1,10 @@ +package info.nightscout.sdk.exceptions + +import java.io.IOException + +abstract class NightscoutException : IOException { + constructor() : super() + constructor(message: String) : super(message) + constructor(message: String, cause: Throwable) : super(message, cause) + constructor(cause: Throwable?) : super(cause) +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/exceptions/TodoNightscoutException.kt b/ns-sdk/src/main/java/info/nightscout/sdk/exceptions/TodoNightscoutException.kt new file mode 100644 index 0000000000..e90a172319 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/exceptions/TodoNightscoutException.kt @@ -0,0 +1,3 @@ +package info.nightscout.sdk.exceptions + +class TodoNightscoutException : NightscoutException() diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidCallbackClient.kt b/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidCallbackClient.kt new file mode 100644 index 0000000000..a21b12e964 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidCallbackClient.kt @@ -0,0 +1,26 @@ +package info.nightscout.sdk.interfaces + +import info.nightscout.sdk.localmodel.Status +import kotlinx.coroutines.Job + +interface NSAndroidCallbackClient { + + interface NSCallback { + + fun onSuccess(value: T) + fun onFailure(exception: Exception) + } + + interface NSCancellable { + + fun cancel() + } + + class NSJobCancellable(val job: Job) : NSCancellable { + + override fun cancel() = job.cancel() + } + + fun getStatus(callback: NSCallback): NSCancellable +} + diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidClient.kt b/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidClient.kt new file mode 100644 index 0000000000..918f4e8538 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidClient.kt @@ -0,0 +1,21 @@ +package info.nightscout.sdk.interfaces + +import info.nightscout.sdk.localmodel.Status +import info.nightscout.sdk.localmodel.entry.NSSgvV3 +import info.nightscout.sdk.localmodel.treatment.NSTreatment +import info.nightscout.sdk.remotemodel.LastModified +import info.nightscout.sdk.remotemodel.RemoteDeviceStatus + +interface NSAndroidClient { + + suspend fun getVersion(): String + suspend fun getStatus(): Status + suspend fun getEntries(): String + + suspend fun getLastModified(): LastModified + suspend fun getSgvs(): List + suspend fun getSgvsModifiedSince(from: Long): List + suspend fun getSgvsNewerThan(from: Long, limit: Long): List + suspend fun getTreatmentsModifiedSince(from: Long, limit: Long): List + suspend fun getDeviceStatusModifiedSince(from: Long): List +} \ No newline at end of file diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidRxClient.kt b/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidRxClient.kt new file mode 100644 index 0000000000..77a88358a1 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidRxClient.kt @@ -0,0 +1,19 @@ +package info.nightscout.sdk.interfaces + +import info.nightscout.sdk.localmodel.Status +import info.nightscout.sdk.localmodel.entry.NSSgvV3 +import info.nightscout.sdk.localmodel.treatment.NSTreatment +import info.nightscout.sdk.remotemodel.LastModified +import info.nightscout.sdk.remotemodel.RemoteDeviceStatus +import io.reactivex.rxjava3.core.Single + +interface NSAndroidRxClient { + + fun getVersion(): Single + fun getStatus(): Single + fun getLastModified(): Single + fun getSgvsModifiedSince(from: Long): Single> + fun getTreatmentsModifiedSince(from: Long, limit: Long): Single> + fun getDeviceStatusModifiedSince(from: Long): Single> +} + diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/ApiPermission.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/ApiPermission.kt new file mode 100644 index 0000000000..e84e3f3faf --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/ApiPermission.kt @@ -0,0 +1,11 @@ +package info.nightscout.sdk.localmodel + +data class ApiPermission( + val create: Boolean, + val read: Boolean, + val update: Boolean, + val delete: Boolean +) { + val full: Boolean + get() = this.create && this.read && this.update && this.delete +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/ApiPermissions.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/ApiPermissions.kt new file mode 100644 index 0000000000..d78861667a --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/ApiPermissions.kt @@ -0,0 +1,10 @@ +package info.nightscout.sdk.localmodel + +data class ApiPermissions( + val deviceStatus: ApiPermission, + val entries: ApiPermission, + val food: ApiPermission, + val profile: ApiPermission, + val settings: ApiPermission, + val treatments: ApiPermission +) diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/Status.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/Status.kt new file mode 100644 index 0000000000..12a497a5d1 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/Status.kt @@ -0,0 +1,9 @@ +package info.nightscout.sdk.localmodel + +data class Status( + val version: String, + val apiVersion: String, + val srvDate: Long, + val storage: Storage, + val apiPermissions: ApiPermissions +) diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/Storage.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/Storage.kt new file mode 100644 index 0000000000..f8b6ce2224 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/Storage.kt @@ -0,0 +1,3 @@ +package info.nightscout.sdk.localmodel + +data class Storage(val storage: String, val version: String) diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/entry/Direction.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/entry/Direction.kt new file mode 100644 index 0000000000..6fd2c91651 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/entry/Direction.kt @@ -0,0 +1,51 @@ +package info.nightscout.sdk.localmodel.entry + +enum class Direction(val nsName: String, val txtIcon: String) { + TRIPLE_DOWN("TripleDown", "\u290B"), // ⤋ + DOUBLE_DOWN("DoubleDown", "\u21ca"), // ⇊ + SINGLE_DOWN("SingleDown", "\u2193"), // ↓ + FORTY_FIVE_DOWN("FortyFiveDown", "\u2198"), // ↘ + FLAT("Flat", "\u2192"), // → + FORTY_FIVE_UP("FortyFiveUp", "\u2197"), // ↗ + SINGLE_UP("SingleUp", "\u2191"), // ↑ + DOUBLE_UP("DoubleUp", "\u21c8"), // ⇈ + TRIPLE_UP("TripleUp", "\u290A"), // ⤊ + NONE("NONE", "⇼"), // + INVALID("", "-"), // +} + +/* + +Nightscout: + NONE: '⇼' + , TripleUp: '⤊' \u290A + , DoubleUp: '⇈' + , SingleUp: '↑' + , FortyFiveUp: '↗' + , Flat: '→' + , FortyFiveDown: '↘' + , SingleDown: '↓' + , DoubleDown: '⇊' + , TripleDown: '⤋' \u290B + , 'NOT COMPUTABLE': '-' + , 'RATE OUT OF RANGE': '⇕' \u21D5 + + xDrip: + + + if (slope_name.compareTo("DoubleDown") == 0) { + slope_by_minute = -3.5; + } else if (slope_name.compareTo("SingleDown") == 0) { + slope_by_minute = -2; + } else if (slope_name.compareTo("FortyFiveDown") == 0) { + slope_by_minute = -1; + } else if (slope_name.compareTo("Flat") == 0) { + slope_by_minute = 0; + } else if (slope_name.compareTo("FortyFiveUp") == 0) { + slope_by_minute = 2; + } else if (slope_name.compareTo("SingleUp") == 0) { + slope_by_minute = 3.5; + } else if (slope_name.compareTo("DoubleUp") == 0) { + + + */ diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/entry/Entry.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/entry/Entry.kt new file mode 100644 index 0000000000..561df012e8 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/entry/Entry.kt @@ -0,0 +1,13 @@ +package info.nightscout.sdk.localmodel.entry + +interface Entry { + val date: Long + val device: String? + val identifier: String + val srvModified: Long + val srvCreated: Long + val utcOffset: Long? + val subject: String? + var isReadOnly: Boolean // TODO: nullability? + val isValid: Boolean +} \ No newline at end of file diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/entry/NSSgvV3.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/entry/NSSgvV3.kt new file mode 100644 index 0000000000..8edcce0743 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/entry/NSSgvV3.kt @@ -0,0 +1,20 @@ +package info.nightscout.sdk.localmodel.entry + +data class NSSgvV3( + override val date: Long, + override val device: String?, + override val identifier: String, + override val srvModified: Long, + override val srvCreated: Long, + override val utcOffset: Long?, + override val subject: String?, + override var isReadOnly: Boolean, + override val isValid: Boolean, + val sgv: Double, // TODO: might be Double? + val units: NsUnits, + val direction: Direction, + val noise: Int?, // TODO: enum? + val filtered: Double?, // number in doc (I found decimal values in API v1 + val unfiltered: Double?, // number in doc (I found decimal values in API v1 + // TODO: add SVG fields +) : Entry diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/entry/NsUnits.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/entry/NsUnits.kt new file mode 100644 index 0000000000..8498c390ac --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/entry/NsUnits.kt @@ -0,0 +1,12 @@ +package info.nightscout.sdk.localmodel.entry + +enum class NsUnits(val value: String) { + MG_DL("mg/dl"), + MMOL_L("mmol") + ; + + companion object { + + fun fromString(name: String?) = values().firstOrNull { it.value == name } ?: MG_DL + } +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/EventType.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/EventType.kt new file mode 100644 index 0000000000..4f6d8620e5 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/EventType.kt @@ -0,0 +1,43 @@ +package info.nightscout.sdk.localmodel.treatment + +import com.google.gson.annotations.SerializedName + +@Suppress("unused") +enum class EventType(val text: String) { + + @SerializedName("Site Change") CANNULA_CHANGE("Site Change"), + @SerializedName("Insulin Change") INSULIN_CHANGE("Insulin Change"), + @SerializedName("Pump Battery Change") PUMP_BATTERY_CHANGE("Pump Battery Change"), + @SerializedName("Sensor Change") SENSOR_CHANGE("Sensor Change"), + @SerializedName("Sensor Start") SENSOR_STARTED("Sensor Start"), + @SerializedName("Sensor Stop") SENSOR_STOPPED("Sensor Stop"), + @SerializedName("BG Check") FINGER_STICK_BG_VALUE("BG Check"), + @SerializedName("Exercise") EXERCISE("Exercise"), + @SerializedName("Announcement") ANNOUNCEMENT("Announcement"), + @SerializedName("Question") QUESTION("Question"), + @SerializedName("Note") NOTE("Note"), + @SerializedName("OpenAPS Offline") APS_OFFLINE("OpenAPS Offline"), + @SerializedName("D.A.D. Alert") DAD_ALERT("D.A.D. Alert"), + @SerializedName("Mbg") NS_MBG("Mbg"), + + // Used but not as a Therapy Event (use constants only) + @SerializedName("Carb Correction") CARBS_CORRECTION("Carb Correction"), + @SerializedName("Bolus Wizard") BOLUS_WIZARD("Bolus Wizard"), + @SerializedName("Correction Bolus") CORRECTION_BOLUS("Correction Bolus"), + @SerializedName("Meal Bolus") MEAL_BOLUS("Meal Bolus"), + @SerializedName("Combo Bolus") COMBO_BOLUS("Combo Bolus"), + @SerializedName("Temporary Target") TEMPORARY_TARGET("Temporary Target"), + @SerializedName("Temporary Target Cancel") TEMPORARY_TARGET_CANCEL("Temporary Target Cancel"), + @SerializedName("Profile Switch") PROFILE_SWITCH("Profile Switch"), + @SerializedName("Snack Bolus") SNACK_BOLUS("Snack Bolus"), + @SerializedName("Temp Basal") TEMPORARY_BASAL("Temp Basal"), + @SerializedName("Temp Basal Start") TEMPORARY_BASAL_START("Temp Basal Start"), + @SerializedName("Temp Basal End") TEMPORARY_BASAL_END("Temp Basal End"), + + @SerializedName("") NONE(""); + + companion object { + + fun fromString(text: String?) = values().firstOrNull { it.text == text } ?: NONE + } +} \ No newline at end of file diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/GlucoseType.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/GlucoseType.kt new file mode 100644 index 0000000000..bec75d0161 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/GlucoseType.kt @@ -0,0 +1,9 @@ +package info.nightscout.sdk.localmodel.treatment + +import com.google.gson.annotations.SerializedName + +enum class GlucoseType { + @SerializedName("Sensor") Sensor, + @SerializedName("Finger") Finger, + @SerializedName("Manual") Manual +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSBolus.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSBolus.kt new file mode 100644 index 0000000000..76d482f222 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSBolus.kt @@ -0,0 +1,36 @@ +package info.nightscout.sdk.localmodel.treatment + +import info.nightscout.sdk.localmodel.entry.NsUnits + +data class NSBolus( + override val date: Long, + override val device: String?, + override val identifier: String, + override val units: NsUnits?, + override val srvModified: Long, + override val srvCreated: Long, + override val utcOffset: Long, + override val subject: String?, + override var isReadOnly: Boolean, + override val isValid: Boolean, + override val eventType: EventType, + override val notes: String?, + override val pumpId: Long?, + override val endId: Long?, + override val pumpType: String?, + override val pumpSerial: String?, + val insulin: Double, + val type: BolusType + +) : NSTreatment { + enum class BolusType { + NORMAL, + SMB, + PRIMING; + + companion object { + + fun fromString(name: String?) = values().firstOrNull { it.name == name } ?: NORMAL + } + } +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSBolusWizard.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSBolusWizard.kt new file mode 100644 index 0000000000..11a1ab3a48 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSBolusWizard.kt @@ -0,0 +1,25 @@ +package info.nightscout.sdk.localmodel.treatment + +import info.nightscout.sdk.localmodel.entry.NsUnits +import org.json.JSONObject + +data class NSBolusWizard( + override val date: Long, + override val device: String?, + override val identifier: String, + override val units: NsUnits?, + override val srvModified: Long, + override val srvCreated: Long, + override val utcOffset: Long, + override val subject: String?, + override var isReadOnly: Boolean, + override val isValid: Boolean, + override val eventType: EventType, + override val notes: String?, + override val pumpId: Long?, + override val endId: Long?, + override val pumpType: String?, + override val pumpSerial: String?, + val bolusCalculatorResult: String?, + val glucose: Double?, +) : NSTreatment \ No newline at end of file diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSCarbs.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSCarbs.kt new file mode 100644 index 0000000000..9f3dd66be2 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSCarbs.kt @@ -0,0 +1,24 @@ +package info.nightscout.sdk.localmodel.treatment + +import info.nightscout.sdk.localmodel.entry.NsUnits + +data class NSCarbs( + override val date: Long, + override val device: String?, + override val identifier: String, + override val units: NsUnits?, + override val srvModified: Long, + override val srvCreated: Long, + override val utcOffset: Long, + override val subject: String?, + override var isReadOnly: Boolean, + override val isValid: Boolean, + override val eventType: EventType, + override val notes: String?, + override val pumpId: Long?, + override val endId: Long?, + override val pumpType: String?, + override val pumpSerial: String?, + val carbs: Double, + val duration: Long +) : NSTreatment \ No newline at end of file diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSEffectiveProfileSwitch.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSEffectiveProfileSwitch.kt new file mode 100644 index 0000000000..4fa8b1e322 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSEffectiveProfileSwitch.kt @@ -0,0 +1,31 @@ +package info.nightscout.sdk.localmodel.treatment + +import info.nightscout.sdk.localmodel.entry.NsUnits +import org.json.JSONObject + +data class NSEffectiveProfileSwitch( + override val date: Long, + override val device: String?, + override val identifier: String, + override val units: NsUnits?, + override val srvModified: Long, + override val srvCreated: Long, + override val utcOffset: Long, + override val subject: String?, + override var isReadOnly: Boolean, + override val isValid: Boolean, + override val eventType: EventType, + override val notes: String?, + override val pumpId: Long?, + override val endId: Long?, + override val pumpType: String?, + override val pumpSerial: String?, + val profileJson: JSONObject, + val originalProfileName: String, + val originalCustomizedName: String, + val originalTimeshift: Long, + val originalPercentage: Int, + val originalDuration: Long, + val originalEnd: Long + +) : NSTreatment \ No newline at end of file diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSExtendedBolus.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSExtendedBolus.kt new file mode 100644 index 0000000000..0fc041d9e5 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSExtendedBolus.kt @@ -0,0 +1,25 @@ +package info.nightscout.sdk.localmodel.treatment + +import info.nightscout.sdk.localmodel.entry.NsUnits + +data class NSExtendedBolus( + override val date: Long, + override val device: String?, + override val identifier: String, + override val units: NsUnits?, + override val srvModified: Long, + override val srvCreated: Long, + override val utcOffset: Long, + override val subject: String?, + override var isReadOnly: Boolean, + override val isValid: Boolean, + override val eventType: EventType, + override val notes: String?, + override val pumpId: Long?, + override val endId: Long?, + override val pumpType: String?, + override val pumpSerial: String?, + val duration: Long, + val enteredinsulin: Double, + val isEmulatingTempbasal: Boolean +) : NSTreatment diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSOfflineEvent.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSOfflineEvent.kt new file mode 100644 index 0000000000..1abffef44c --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSOfflineEvent.kt @@ -0,0 +1,39 @@ +package info.nightscout.sdk.localmodel.treatment + +import info.nightscout.sdk.localmodel.entry.NsUnits + +data class NSOfflineEvent( + override val date: Long, + override val device: String?, + override val identifier: String, + override val units: NsUnits?, + override val srvModified: Long, + override val srvCreated: Long, + override val utcOffset: Long, + override val subject: String?, + override var isReadOnly: Boolean, + override val isValid: Boolean, + override val eventType: EventType, + override val notes: String?, + override val pumpId: Long?, + override val endId: Long?, + override val pumpType: String?, + override val pumpSerial: String?, + val duration: Long, + val reason: Reason +) : NSTreatment { + + enum class Reason { + DISCONNECT_PUMP, + SUSPEND, + DISABLE_LOOP, + SUPER_BOLUS, + OTHER + ; + + companion object { + + fun fromString(reason: String?) = values().firstOrNull { it.name == reason } ?: OTHER + } + } +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSProfileSwitch.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSProfileSwitch.kt new file mode 100644 index 0000000000..c97e5de38c --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSProfileSwitch.kt @@ -0,0 +1,30 @@ +package info.nightscout.sdk.localmodel.treatment + +import info.nightscout.sdk.localmodel.entry.NsUnits +import org.json.JSONObject + +data class NSProfileSwitch( + override val date: Long, + override val device: String?, + override val identifier: String, + override val units: NsUnits?, + override val srvModified: Long, + override val srvCreated: Long, + override val utcOffset: Long, + override val subject: String?, + override var isReadOnly: Boolean, + override val isValid: Boolean, + override val eventType: EventType, + override val notes: String?, + override val pumpId: Long?, + override val endId: Long?, + override val pumpType: String?, + override val pumpSerial: String?, + val profileJson: JSONObject?, + val profileName: String, + val originalProfileName: String?, + val timeShift: Long?, + val percentage: Int?, + val duration: Long?, + val originalDuration: Long? +) : NSTreatment \ No newline at end of file diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSTemporaryBasal.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSTemporaryBasal.kt new file mode 100644 index 0000000000..8a025e7097 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSTemporaryBasal.kt @@ -0,0 +1,43 @@ +package info.nightscout.sdk.localmodel.treatment + +import info.nightscout.sdk.localmodel.entry.NsUnits +import org.json.JSONObject + +data class NSTemporaryBasal( + override val date: Long, + override val device: String?, + override val identifier: String, + override val units: NsUnits?, + override val srvModified: Long, + override val srvCreated: Long, + override val utcOffset: Long, + override val subject: String?, + override var isReadOnly: Boolean, + override val isValid: Boolean, + override val eventType: EventType, + override val notes: String?, + override val pumpId: Long?, + override val endId: Long?, + override val pumpType: String?, + override val pumpSerial: String?, + val duration: Long, + val rate: Double, + val isAbsolute: Boolean, + val type: Type +) : NSTreatment { + + enum class Type { + NORMAL, + EMULATED_PUMP_SUSPEND, + PUMP_SUSPEND, + SUPERBOLUS, + FAKE_EXTENDED // in memory only + ; + + companion object { + + fun fromString(name: String?) = values().firstOrNull { it.name == name } ?: NORMAL + } + } + +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSTemporaryTarget.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSTemporaryTarget.kt new file mode 100644 index 0000000000..c4a03bca18 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSTemporaryTarget.kt @@ -0,0 +1,45 @@ +package info.nightscout.sdk.localmodel.treatment + +import info.nightscout.sdk.localmodel.entry.NsUnits + +data class NSTemporaryTarget( + override val date: Long, + override val device: String?, + override val identifier: String, + override val units: NsUnits?, + override val srvModified: Long, + override val srvCreated: Long, + override val utcOffset: Long, + override val subject: String?, + override var isReadOnly: Boolean, + override val isValid: Boolean, + override val eventType: EventType, + override val notes: String?, + override val pumpId: Long?, + override val endId: Long?, + override val pumpType: String?, + override val pumpSerial: String?, + val duration: Long, + val targetBottom: Double, + val targetTop: Double, + val reason: Reason, + + ) : NSTreatment { + + fun targetBottomAsMgdl() = targetBottom.asMgdl() + fun targetTopAsMgdl() = targetTop.asMgdl() + enum class Reason(val text: String) { + CUSTOM("Custom"), + HYPOGLYCEMIA("Hypo"), + ACTIVITY("Activity"), + EATING_SOON("Eating Soon"), + AUTOMATION("Automation"), + WEAR("Wear") + ; + + companion object { + + fun fromString(reason: String?) = values().firstOrNull { it.text == reason } ?: CUSTOM + } + } +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSTherapyEvent.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSTherapyEvent.kt new file mode 100644 index 0000000000..503c9ee65f --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSTherapyEvent.kt @@ -0,0 +1,40 @@ +package info.nightscout.sdk.localmodel.treatment + +import com.google.gson.annotations.SerializedName +import info.nightscout.sdk.localmodel.entry.NsUnits + +data class NSTherapyEvent( + override val date: Long, + override val device: String?, + override val identifier: String, + override val units: NsUnits?, + override val srvModified: Long, + override val srvCreated: Long, + override val utcOffset: Long, + override val subject: String?, + override var isReadOnly: Boolean, + override val isValid: Boolean, + override val eventType: EventType, + override val notes: String?, + override val pumpId: Long?, + override val endId: Long?, + override val pumpType: String?, + override val pumpSerial: String?, + val duration: Long, + var enteredBy: String? = null, + var glucose: Double? = null, + var glucoseType: MeterType? = null, +) : NSTreatment { + + enum class MeterType(val text: String) { + @SerializedName("Finger") FINGER("Finger"), + @SerializedName("Sensor") SENSOR("Sensor"), + @SerializedName("Manual") MANUAL("Manual") + ; + + companion object { + + fun fromString(text: String?) = values().firstOrNull { it.text == text } ?: MANUAL + } + } +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSTreatment.kt b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSTreatment.kt new file mode 100644 index 0000000000..f522b0673c --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/localmodel/treatment/NSTreatment.kt @@ -0,0 +1,29 @@ +package info.nightscout.sdk.localmodel.treatment + +import info.nightscout.sdk.localmodel.entry.NsUnits + +interface NSTreatment { + val date: Long + val device: String? + val identifier: String + val units: NsUnits? + val eventType: EventType + val srvModified: Long + val srvCreated: Long + val utcOffset: Long + val subject: String? + var isReadOnly: Boolean + val isValid: Boolean + val notes: String? + val pumpId: Long? + val endId: Long? + val pumpType: String? + val pumpSerial: String? + + fun Double.asMgdl() = + when (units) { + NsUnits.MG_DL -> this + NsUnits.MMOL_L -> this * 18 + null -> this + } +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/mapper/ApiPermissionMapper.kt b/ns-sdk/src/main/java/info/nightscout/sdk/mapper/ApiPermissionMapper.kt new file mode 100644 index 0000000000..b407cb1763 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/mapper/ApiPermissionMapper.kt @@ -0,0 +1,32 @@ +package info.nightscout.sdk.mapper + +import info.nightscout.sdk.localmodel.ApiPermission +import info.nightscout.sdk.localmodel.ApiPermissions +import info.nightscout.sdk.remotemodel.RemoteApiPermission +import info.nightscout.sdk.remotemodel.RemoteApiPermissions +import info.nightscout.sdk.remotemodel.read + +internal fun RemoteApiPermissions.toLocal(): ApiPermissions = + ApiPermissions( + deviceStatus = deviceStatus.toLocal(), + entries = entries.toLocal(), + food = food.toLocal(), + profile = profile.toLocal(), + settings = settings.toLocal(), + treatments = treatments.toLocal() + ) + +internal fun RemoteApiPermission.toLocal(): ApiPermission = + ApiPermission(create = create, read = read, update = update, delete = delete) + +internal val RemoteApiPermission.create: Boolean + get() = this.contains('c') + +internal val RemoteApiPermission.read: Boolean + get() = this.contains('r') + +internal val RemoteApiPermission.update: Boolean + get() = this.contains('u') + +internal val RemoteApiPermission.delete: Boolean + get() = this.contains('d') diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/mapper/StatusResponseMapper.kt b/ns-sdk/src/main/java/info/nightscout/sdk/mapper/StatusResponseMapper.kt new file mode 100644 index 0000000000..1c9f5b419a --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/mapper/StatusResponseMapper.kt @@ -0,0 +1,12 @@ +package info.nightscout.sdk.mapper + +import info.nightscout.sdk.localmodel.Status +import info.nightscout.sdk.remotemodel.RemoteStatusResponse + +internal fun RemoteStatusResponse.toLocal() = Status( + version = version, + apiVersion = apiVersion, + srvDate = srvDate, + storage = storage.toLocal(), + apiPermissions = apiPermissions.toLocal() +) diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/mapper/StorageMapper.kt b/ns-sdk/src/main/java/info/nightscout/sdk/mapper/StorageMapper.kt new file mode 100644 index 0000000000..c2d26f81e2 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/mapper/StorageMapper.kt @@ -0,0 +1,6 @@ +package info.nightscout.sdk.mapper + +import info.nightscout.sdk.localmodel.Storage +import info.nightscout.sdk.remotemodel.RemoteStorage + +internal fun RemoteStorage.toLocal() = Storage(storage = storage, version = version) diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/mapper/SvgMapper.kt b/ns-sdk/src/main/java/info/nightscout/sdk/mapper/SvgMapper.kt new file mode 100644 index 0000000000..b91debc392 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/mapper/SvgMapper.kt @@ -0,0 +1,34 @@ +package info.nightscout.sdk.mapper + +import info.nightscout.sdk.localmodel.entry.Direction +import info.nightscout.sdk.localmodel.entry.NSSgvV3 +import info.nightscout.sdk.localmodel.entry.NsUnits +import info.nightscout.sdk.remotemodel.RemoteEntry + +@JvmSynthetic +internal fun RemoteEntry.toSgv(): NSSgvV3? { + + this.sgv ?: return null + if (this.type != "sgv") return null + + return NSSgvV3( + date = this.date, + device = this.device, + identifier = this.identifier, + srvModified = this.srvModified, + srvCreated = this.srvCreated, + utcOffset = this.utcOffset ?: 0, + subject = this.subject, + direction = this.direction.toDirection(), + sgv = this.sgv, + isReadOnly = this.isReadOnly ?: false, + isValid = this.isValid ?: true, + noise = this.noise, // TODO: to Enum? + filtered = this.filtered, + unfiltered = this.unfiltered, + units = NsUnits.fromString(this.units) + ) +} + +private fun String?.toDirection(): Direction = + Direction.values().firstOrNull { it.nsName == this } ?: Direction.INVALID diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/mapper/TreatmentMapper.kt b/ns-sdk/src/main/java/info/nightscout/sdk/mapper/TreatmentMapper.kt new file mode 100644 index 0000000000..7af6badcbb --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/mapper/TreatmentMapper.kt @@ -0,0 +1,338 @@ +package info.nightscout.sdk.mapper + +import info.nightscout.sdk.localmodel.entry.NsUnits +import info.nightscout.sdk.localmodel.treatment.EventType +import info.nightscout.sdk.localmodel.treatment.NSBolus +import info.nightscout.sdk.localmodel.treatment.NSBolusWizard +import info.nightscout.sdk.localmodel.treatment.NSCarbs +import info.nightscout.sdk.localmodel.treatment.NSEffectiveProfileSwitch +import info.nightscout.sdk.localmodel.treatment.NSExtendedBolus +import info.nightscout.sdk.localmodel.treatment.NSOfflineEvent +import info.nightscout.sdk.localmodel.treatment.NSProfileSwitch +import info.nightscout.sdk.localmodel.treatment.NSTemporaryBasal +import info.nightscout.sdk.localmodel.treatment.NSTemporaryTarget +import info.nightscout.sdk.localmodel.treatment.NSTherapyEvent +import info.nightscout.sdk.localmodel.treatment.NSTreatment +import info.nightscout.sdk.remotemodel.RemoteTreatment +import org.json.JSONObject +import java.util.concurrent.TimeUnit + +internal fun RemoteTreatment.toTreatment(): NSTreatment? { + val treatmentTimestamp = timestamp() + when { + insulin != null && insulin > 0 -> + return NSBolus( + date = treatmentTimestamp, + device = this.device, + identifier = this.identifier, + units = NsUnits.fromString(this.units), + srvModified = this.srvModified, + srvCreated = this.srvCreated, + utcOffset = this.utcOffset ?: 0, + subject = this.subject, + isReadOnly = this.isReadOnly ?: false, + isValid = this.isValid ?: true, + eventType = this.eventType, + notes = this.notes, + pumpId = this.pumpId, + endId = this.endId, + pumpType = this.pumpType, + pumpSerial = this.pumpSerial, + insulin = this.insulin, + type = NSBolus.BolusType.fromString(this.type), + ) + + carbs != null && carbs > 0 -> + return NSCarbs( + date = treatmentTimestamp, + device = this.device, + identifier = this.identifier, + units = NsUnits.fromString(this.units), + srvModified = this.srvModified, + srvCreated = this.srvCreated, + utcOffset = this.utcOffset ?: 0, + subject = this.subject, + isReadOnly = this.isReadOnly ?: false, + isValid = this.isValid ?: true, + eventType = this.eventType, + notes = this.notes, + pumpId = this.pumpId, + endId = this.endId, + pumpType = this.pumpType, + pumpSerial = this.pumpSerial, + carbs = this.carbs, + duration = this.duration ?: 0L + ) + + eventType == EventType.TEMPORARY_TARGET -> { + if (treatmentTimestamp == 0L) return null + + this.duration ?: return null + this.targetBottom ?: return null + this.targetTop ?: return null + + return NSTemporaryTarget( + date = treatmentTimestamp, + device = this.device, + identifier = this.identifier, + units = NsUnits.fromString(this.units), + srvModified = this.srvModified, + srvCreated = this.srvCreated, + utcOffset = this.utcOffset ?: 0, + subject = this.subject, + isReadOnly = this.isReadOnly ?: false, + isValid = this.isValid ?: true, + eventType = this.eventType, + notes = this.notes, + pumpId = this.pumpId, + endId = this.endId, + pumpType = this.pumpType, + pumpSerial = this.pumpSerial, + duration = this.durationInMilliseconds ?: TimeUnit.MINUTES.toMillis(this.duration), + targetBottom = this.targetBottom, + targetTop = this.targetTop, + reason = NSTemporaryTarget.Reason.fromString(this.reason) + ) + } + + // Convert back emulated TBR -> EB + eventType == EventType.TEMPORARY_BASAL && extendedEmulated != null -> { + + return NSExtendedBolus( + date = treatmentTimestamp, + device = device, + identifier = identifier, + units = NsUnits.fromString(extendedEmulated.units), + srvModified = srvModified, + srvCreated = srvCreated, + utcOffset = utcOffset ?: 0, + subject = subject, + isReadOnly = extendedEmulated.isReadOnly ?: false, + isValid = extendedEmulated.isValid ?: true, + eventType = extendedEmulated.eventType, + notes = extendedEmulated.notes, + pumpId = extendedEmulated.pumpId, + endId = extendedEmulated.endId, + pumpType = extendedEmulated.pumpType, + pumpSerial = extendedEmulated.pumpSerial, + enteredinsulin = extendedEmulated.enteredinsulin ?: 0.0, + duration = extendedEmulated.durationInMilliseconds ?: TimeUnit.MINUTES.toMillis(extendedEmulated.duration ?: 0L), + isEmulatingTempbasal = extendedEmulated.isEmulatingTempBasal + ) + } + + eventType == EventType.TEMPORARY_BASAL -> { + if (treatmentTimestamp == 0L) return null + + this.absolute ?: this.percent ?: return null + this.duration ?: return null + if (this.duration == 0L && this.durationInMilliseconds == null) return null + + return NSTemporaryBasal( + date = treatmentTimestamp, + device = this.device, + identifier = this.identifier, + units = NsUnits.fromString(this.units), + srvModified = this.srvModified, + srvCreated = this.srvCreated, + utcOffset = this.utcOffset ?: 0, + subject = this.subject, + isReadOnly = this.isReadOnly ?: false, + isValid = this.isValid ?: true, + eventType = this.eventType, + notes = this.notes, + pumpId = this.pumpId, + endId = this.endId, + pumpType = this.pumpType, + pumpSerial = this.pumpSerial, + duration = this.durationInMilliseconds ?: TimeUnit.MINUTES.toMillis(this.duration), + isAbsolute = this.absolute != null, + rate = this.absolute ?: (this.percent?.plus(100.0)) ?: 0.0, + type = NSTemporaryBasal.Type.fromString(this.type) + ) + } + + eventType == EventType.NOTE && this.originalProfileName != null -> { + if (treatmentTimestamp == 0L) return null + this.profileJson ?: return null + this.originalCustomizedName ?: return null + this.originalTimeshift ?: return null + this.originalPercentage ?: return null + this.originalDuration ?: return null + this.originalEnd ?: return null + + return NSEffectiveProfileSwitch( + date = treatmentTimestamp, + device = this.device, + identifier = this.identifier, + units = NsUnits.fromString(this.units), + srvModified = this.srvModified, + srvCreated = this.srvCreated, + utcOffset = this.utcOffset ?: 0, + subject = this.subject, + isReadOnly = this.isReadOnly ?: false, + isValid = this.isValid ?: true, + eventType = this.eventType, + notes = this.notes, + pumpId = this.pumpId, + endId = this.endId, + pumpType = this.pumpType, + pumpSerial = this.pumpSerial, + profileJson = JSONObject(this.profileJson), + originalProfileName = this.originalProfileName, + originalCustomizedName = this.originalCustomizedName, + originalTimeshift = this.originalTimeshift, + originalPercentage = this.originalPercentage, + originalDuration = this.originalDuration, + originalEnd = this.originalEnd + ) + } + + eventType == EventType.PROFILE_SWITCH -> { + if (treatmentTimestamp == 0L) return null + this.profile ?: return null + + return NSProfileSwitch( + date = treatmentTimestamp, + device = this.device, + identifier = this.identifier, + units = NsUnits.fromString(this.units), + srvModified = this.srvModified, + srvCreated = this.srvCreated, + utcOffset = this.utcOffset ?: 0, + subject = this.subject, + isReadOnly = this.isReadOnly ?: false, + isValid = this.isValid ?: true, + eventType = this.eventType, + notes = this.notes, + pumpId = this.pumpId, + endId = this.endId, + pumpType = this.pumpType, + pumpSerial = this.pumpSerial, + profileJson = this.profileJson?.let { JSONObject(this.profileJson) }, + profileName = this.profile, + originalProfileName = this.originalProfileName, + originalDuration = this.originalDuration, + duration = this.duration, + timeShift = this.timeshift, + percentage = this.percentage, + ) + } + + eventType == EventType.BOLUS_WIZARD -> { + if (treatmentTimestamp == 0L) return null + this.bolusCalculatorResult ?: return null + + return NSBolusWizard( + date = treatmentTimestamp, + device = this.device, + identifier = this.identifier, + units = NsUnits.fromString(this.units), + srvModified = this.srvModified, + srvCreated = this.srvCreated, + utcOffset = this.utcOffset ?: 0, + subject = this.subject, + isReadOnly = this.isReadOnly ?: false, + isValid = this.isValid ?: true, + eventType = this.eventType, + notes = this.notes, + pumpId = this.pumpId, + endId = this.endId, + pumpType = this.pumpType, + pumpSerial = this.pumpSerial, + bolusCalculatorResult = this.bolusCalculatorResult, + glucose = this.glucose + ) + } + + eventType == EventType.CANNULA_CHANGE || + eventType == EventType.INSULIN_CHANGE || + eventType == EventType.SENSOR_CHANGE || + eventType == EventType.FINGER_STICK_BG_VALUE || + eventType == EventType.NONE || + eventType == EventType.ANNOUNCEMENT || + eventType == EventType.QUESTION || + eventType == EventType.EXERCISE || + eventType == EventType.NOTE || + eventType == EventType.PUMP_BATTERY_CHANGE -> { + if (treatmentTimestamp == 0L) return null + + return NSTherapyEvent( + date = treatmentTimestamp, + device = this.device, + identifier = this.identifier, + units = NsUnits.fromString(this.units), + srvModified = this.srvModified, + srvCreated = this.srvCreated, + utcOffset = this.utcOffset ?: 0, + subject = this.subject, + isReadOnly = this.isReadOnly ?: false, + isValid = this.isValid ?: true, + eventType = this.eventType, + notes = this.notes, + pumpId = this.pumpId, + endId = this.endId, + pumpType = this.pumpType, + pumpSerial = this.pumpSerial, + duration = this.durationInMilliseconds ?: TimeUnit.MINUTES.toMillis(this.duration ?: 0L), + glucose = this.glucose, + enteredBy = this.enteredBy, + glucoseType = NSTherapyEvent.MeterType.fromString(this.glucoseType) + ) + } + + eventType == EventType.APS_OFFLINE -> { + if (treatmentTimestamp == 0L) return null + + return NSOfflineEvent( + date = treatmentTimestamp, + device = this.device, + identifier = this.identifier, + units = NsUnits.fromString(this.units), + srvModified = this.srvModified, + srvCreated = this.srvCreated, + utcOffset = this.utcOffset ?: 0, + subject = this.subject, + isReadOnly = this.isReadOnly ?: false, + isValid = this.isValid ?: true, + eventType = this.eventType, + notes = this.notes, + pumpId = this.pumpId, + endId = this.endId, + pumpType = this.pumpType, + pumpSerial = this.pumpSerial, + duration = this.durationInMilliseconds ?: TimeUnit.MINUTES.toMillis(this.duration ?: 0L), + reason = NSOfflineEvent.Reason.fromString(this.reason) + ) + } + + eventType == EventType.COMBO_BOLUS -> { + if (treatmentTimestamp == 0L) return null + this.enteredinsulin ?: return null + + return NSExtendedBolus( + date = treatmentTimestamp, + device = this.device, + identifier = this.identifier, + units = NsUnits.fromString(this.units), + srvModified = this.srvModified, + srvCreated = this.srvCreated, + utcOffset = this.utcOffset ?: 0, + subject = this.subject, + isReadOnly = this.isReadOnly ?: false, + isValid = this.isValid ?: true, + eventType = this.eventType, + notes = this.notes, + pumpId = this.pumpId, + endId = this.endId, + pumpType = this.pumpType, + pumpSerial = this.pumpSerial, + enteredinsulin = this.enteredinsulin, + duration = this.durationInMilliseconds ?: TimeUnit.MINUTES.toMillis(this.duration ?: 0L), + isEmulatingTempbasal = this.isEmulatingTempBasal + ) + } + } + + return null +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/networking/NSAuthInterceptor.kt b/ns-sdk/src/main/java/info/nightscout/sdk/networking/NSAuthInterceptor.kt new file mode 100644 index 0000000000..a5dd72d87b --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/networking/NSAuthInterceptor.kt @@ -0,0 +1,68 @@ +package info.nightscout.sdk.networking + +import info.nightscout.sdk.exceptions.DateHeaderOutOfToleranceException +import info.nightscout.sdk.exceptions.InvalidAccessTokenException +import info.nightscout.sdk.networking.Status.MESSAGE_DATE_HEADER_OUT_OF_TOLERANCE +import info.nightscout.sdk.remotemodel.RemoteAuthResponse +import okhttp3.Interceptor +import okhttp3.Request +import okhttp3.Response +import retrofit2.Retrofit +import java.lang.System.currentTimeMillis + +internal class NSAuthInterceptor(private val refreshToken: String, private val retrofit: Retrofit) : + Interceptor { + + private var jwtToken = "" // the actual Bearer token + + @Suppress("MagicNumber") + override fun intercept(chain: Interceptor.Chain): Response { + + val originalRequest = chain.request() + val authenticationRequest = requestWithBearer(originalRequest) + val initialResponse = chain.proceed(authenticationRequest) + + return when (initialResponse.code) { + 403, 401 -> refreshTokenAndRetry(originalRequest, initialResponse, chain) + else -> initialResponse + } + } + + private fun requestWithBearer(originalRequest: Request): Request = originalRequest.newBuilder() + .addHeader("Date", currentTimeMillis().toString()) + .addHeader("Authorization", "Bearer $jwtToken") + .build() + + @Suppress("MagicNumber") + private fun refreshTokenAndRetry( + originalRequest: Request, + initialResponse: Response, + chain: Interceptor.Chain + ): Response { + + testCanRefresh(initialResponse) + + val authResponseResponse: retrofit2.Response? = retrofit + .create(NightscoutAuthRefreshService::class.java) + .refreshToken(refreshToken) + .execute() + + return when { + authResponseResponse == null -> initialResponse + authResponseResponse.code() in listOf(401, 403) -> throw InvalidAccessTokenException() + authResponseResponse.code() != 200 -> initialResponse + else -> { + authResponseResponse.body()?.token?.let { jwtToken = it } + val newAuthenticationRequest = requestWithBearer(originalRequest) + chain.proceed(newAuthenticationRequest) + } + } + } + + private fun testCanRefresh(initialResponse: Response) { + // Todo: use proper reason code once it is supplied by remote + if (initialResponse.body?.string()?.contains(MESSAGE_DATE_HEADER_OUT_OF_TOLERANCE) == true) { + throw DateHeaderOutOfToleranceException() + } + } +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/networking/NetworkStackBuilder.kt b/ns-sdk/src/main/java/info/nightscout/sdk/networking/NetworkStackBuilder.kt new file mode 100644 index 0000000000..87d7e6ef4a --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/networking/NetworkStackBuilder.kt @@ -0,0 +1,93 @@ +package info.nightscout.sdk.networking + +import android.content.Context +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import okhttp3.Cache +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit + +internal object NetworkStackBuilder { + + @JvmSynthetic + internal fun getApi( + baseUrl: String, + context: Context, + accessToken: String, // refresh token + logging: Boolean = false + ): NightscoutRemoteService = getRetrofit( + baseUrl = baseUrl, + context = context, + refreshToken = accessToken, + logging = logging + ).create(NightscoutRemoteService::class.java) + + private fun getRetrofit( + baseUrl: String, + context: Context, + refreshToken: String, + logging: Boolean + ): Retrofit = + Retrofit.Builder() + .baseUrl("https://$baseUrl/api/") + .client( + getOkHttpClient( + context = context, + logging = logging, + refreshToken = refreshToken, + authRefreshRetrofit = getAuthRefreshRetrofit(baseUrl, context, logging) + ) + ) + .addConverterFactory(GsonConverterFactory.create(provideGson())) + .build() + + private fun getAuthRefreshRetrofit( + baseUrl: String, + context: Context, + logging: Boolean + ): Retrofit = + Retrofit.Builder() + .baseUrl("https://$baseUrl/api/") + .client(getAuthRefreshOkHttpClient(context = context, logging = logging)) + .addConverterFactory(GsonConverterFactory.create(provideGson())) + .build() + + private fun getOkHttpClient( + context: Context, + logging: Boolean, + refreshToken: String, + authRefreshRetrofit: Retrofit + ): OkHttpClient = OkHttpClient.Builder().run { + addInterceptor(NSAuthInterceptor(refreshToken, authRefreshRetrofit)) + commonOkHttpSetup(logging, context) + } + + private fun getAuthRefreshOkHttpClient( + context: Context, + logging: Boolean, + ): OkHttpClient = OkHttpClient.Builder().run { commonOkHttpSetup(logging, context) } + + private fun OkHttpClient.Builder.commonOkHttpSetup( + logging: Boolean, + context: Context + ): OkHttpClient { + if (logging) { + addNetworkInterceptor( + HttpLoggingInterceptor().also { it.level = HttpLoggingInterceptor.Level.BODY } + ) + } + cache(Cache(context.cacheDir, OK_HTTP_CACHE_SIZE)) + readTimeout(OK_HTTP_READ_TIMEOUT, TimeUnit.MILLISECONDS) + writeTimeout(OK_HTTP_WRITE_TIMEOUT, TimeUnit.MILLISECONDS) + return build() + } + + private fun provideGson(): Gson = GsonBuilder().create() + + private const val OK_HTTP_CACHE_SIZE = 10L * 1024 * 1024 + private const val OK_HTTP_READ_TIMEOUT = 60L * 1000 + private const val OK_HTTP_WRITE_TIMEOUT = 60L * 1000 +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/networking/NightscoutAuthRefreshService.kt b/ns-sdk/src/main/java/info/nightscout/sdk/networking/NightscoutAuthRefreshService.kt new file mode 100644 index 0000000000..373de8e6a7 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/networking/NightscoutAuthRefreshService.kt @@ -0,0 +1,16 @@ +package info.nightscout.sdk.networking + +import info.nightscout.sdk.remotemodel.RemoteAuthResponse +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Path + +/** + * Created by adrian on 2019-01-04. + */ + +internal interface NightscoutAuthRefreshService { + + @GET("/api/v2/authorization/request/{refreshToken}") + fun refreshToken(@Path("refreshToken") refreshToken: String): Call +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/networking/NightscoutRemoteService.kt b/ns-sdk/src/main/java/info/nightscout/sdk/networking/NightscoutRemoteService.kt new file mode 100644 index 0000000000..4619aef0c0 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/networking/NightscoutRemoteService.kt @@ -0,0 +1,51 @@ +package info.nightscout.sdk.networking + +import com.google.gson.JsonElement +import info.nightscout.sdk.remotemodel.LastModified +import info.nightscout.sdk.remotemodel.RemoteDeviceStatus +import info.nightscout.sdk.remotemodel.NSResponse +import info.nightscout.sdk.remotemodel.RemoteEntry +import info.nightscout.sdk.remotemodel.RemoteStatusResponse +import info.nightscout.sdk.remotemodel.RemoteTreatment +import retrofit2.Response +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.Query + +/** + * Created by adrian on 2019-12-23. + * + * https://github.com/nightscout/cgm-remote-monitor/blob/master/lib/api3/doc/tutorial.md + * + */ + +internal interface NightscoutRemoteService { + + @GET("v3/status") + // used to get the raw response for more error checking. E.g. to give the user better feedback after new settings. + suspend fun statusVerbose(): Response> + + @GET("v3/status") + suspend fun statusSimple(): NSResponse + + @GET("v3/entries") + suspend fun getEntries(): List + + @GET("v3/lastModified") + suspend fun lastModified(): Response> + + @GET("v3/entries?sort\$desc=date&type=sgv") + suspend fun getSgvs(): Response>> + + @GET("v3/entries") + suspend fun getSgvsNewerThan(@Query(value = "date\$gt", encoded = true) date: Long, @Query("limit") limit: Long): Response>> + + @GET("v3/entries/history/{from}") + suspend fun getSgvsModifiedSince(@Path("from") from: Long): Response>> + + @GET("v3/treatments/history/{from}") + suspend fun getTreatmentsModifiedSince(@Path("from") from: Long, @Query("limit") limit: Long): Response>> + + @GET("v3/devicestatus/history/{from}") + suspend fun getDeviceStatusModifiedSince(@Path("from") from: Long): Response>> +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/networking/Status.kt b/ns-sdk/src/main/java/info/nightscout/sdk/networking/Status.kt new file mode 100644 index 0000000000..d153d37931 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/networking/Status.kt @@ -0,0 +1,6 @@ +package info.nightscout.sdk.networking + +internal object Status { + + const val MESSAGE_DATE_HEADER_OUT_OF_TOLERANCE = "Date header out of tolerance" +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/LastModified.kt b/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/LastModified.kt new file mode 100644 index 0000000000..1f7b3344f4 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/LastModified.kt @@ -0,0 +1,23 @@ +package info.nightscout.sdk.remotemodel + +import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Serializable + +/** + * Timestamp of last modification of every collection + * + **/ +@Serializable +data class LastModified( + @SerializedName("collections") val collections: Collections +) { + + @Serializable + data class Collections( + + @SerializedName("devicestatus") var devicestatus: Long, // devicestatus collection + @SerializedName("entries") var entries: Long, // entries collection + @SerializedName("profile") var profile: Long, // profile collection + @SerializedName("treatments") var treatments: Long // treatments collection + ) +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteAuthResponse.kt b/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteAuthResponse.kt new file mode 100644 index 0000000000..5e8a9fdb9c --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteAuthResponse.kt @@ -0,0 +1,3 @@ +package info.nightscout.sdk.remotemodel + +internal data class RemoteAuthResponse(val token: String, val iat: Long, val exp: Long) diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteDeviceStatus.kt b/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteDeviceStatus.kt new file mode 100644 index 0000000000..26ff3364f0 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteDeviceStatus.kt @@ -0,0 +1,67 @@ +package info.nightscout.sdk.remotemodel + +import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Contextual +import kotlinx.serialization.Serializable +import org.json.JSONObject + +/** + * DeviceStatus coming from uploader or AAPS + * + **/ +@Serializable +data class RemoteDeviceStatus( + @SerializedName("identifier") val identifier: String?, // string Main addressing, required field that identifies document in the collection. The client should not create the identifier, the server automatically assigns it when the document is inserted. + @SerializedName("srvCreated") val srvCreated: Long?, // integer($int64) example: 1525383610088 The server's timestamp of document insertion into the database (Unix epoch in ms). This field appears only for documents which were inserted by API v3. + @SerializedName("srvModified") val srvModified: Long?, // integer($int64) example: 1525383610088 The server's timestamp of the last document modification in the database (Unix epoch in ms). This field appears only for documents which were somehow modified by API v3 (inserted, updated or deleted). + @SerializedName("created_at") val createdAt: String?, // string or string timestamp on previous version of api, in my examples, a lot of treatments don't have date, only created_at, some of them with string others with long... + @SerializedName("uploaderBattery") val uploaderBattery: Int?,// integer($int64) + @SerializedName("device") val device: String?, // "openaps://samsung SM-G970F" + + @SerializedName("uploader") val uploader: Uploader?, + @SerializedName("pump") val pump: Pump?, + @SerializedName("openaps") val openaps: OpenAps?, + @SerializedName("configuration") val configuration: Configuration? +) { + + @Serializable data class Pump( + @SerializedName("clock") val clock: String?, // timestamp in ISO + @SerializedName("reservoir") val reservoir: Double?, + @SerializedName("reservoir_display_override") val reservoirDisplayOverride: String?, + @SerializedName("battery") val battery: Battery?, + @SerializedName("status") val status: Status?, + @Contextual @SerializedName("extended") val extended: JSONObject? // Gson, content depending on pump driver + ) { + + @Serializable data class Battery( + @SerializedName("percent") val percent: Int?, + @SerializedName("voltage") val voltage: Double? + ) + + @Serializable data class Status( + @SerializedName("status") val status: String?, + @SerializedName("timestamp") val timestamp: String? + ) + } + + @Serializable data class OpenAps( + @Contextual @SerializedName("suggested") val suggested: JSONObject?, // Gson + @Contextual @SerializedName("enacted") val enacted: JSONObject?, // Gson + @Contextual @SerializedName("iob") val iob: JSONObject? // Gson + ) + + @Serializable data class Uploader( + @SerializedName("battery") val battery: Int?, + ) + + @Serializable data class Configuration( + @SerializedName("pump") val pump: String?, + @SerializedName("version") val version: String?, + @SerializedName("insulin") val insulin: Int?, + @SerializedName("sensitivity") val sensitivity: Int?, + @Contextual @SerializedName("insulinConfiguration") val insulinConfiguration: JSONObject?, + @Contextual @SerializedName("sensitivityConfiguration") val sensitivityConfiguration: JSONObject?, + @Contextual @SerializedName("overviewConfiguration") val overviewConfiguration: JSONObject?, + @Contextual @SerializedName("safetyConfiguration") val safetyConfiguration: JSONObject? + ) +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteEntry.kt b/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteEntry.kt new file mode 100644 index 0000000000..c4b3135579 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteEntry.kt @@ -0,0 +1,37 @@ +package info.nightscout.sdk.remotemodel + +import com.google.gson.annotations.SerializedName +import info.nightscout.sdk.localmodel.treatment.EventType + +/* +* Depending on the type, different other fields are present. +* Those technically need to be optional. +* +* On upload a sanity check still needs to be done to verify that all mandatory fields for that type are there. +* +* TODO: Find out all types with their optional and mandatory fields +* +* */ +internal data class RemoteEntry( + @SerializedName("type") val type: String, // sgv, mbg, cal, etc; Bolus type NORMAL, SMB, PRIMING + @SerializedName("sgv") val sgv: Double?, // number The glucose reading. (only available for sgv types) + @SerializedName("dateString") val dateString: String, + @SerializedName("date") val date: Long, // required ? TODO: date and dateString are redundant - are both needed? how to handle inconsistency then? Only expose one to clients? + @SerializedName("device") val device: String?, // The device from which the data originated (including serial number of the device, if it is relevant and safe). + @SerializedName("direction") val direction: String?, // TODO: what implicit convention for the directions exists? + @SerializedName("identifier") val identifier: String, + @SerializedName("srvModified") val srvModified: Long, + @SerializedName("srvCreated") val srvCreated: Long, + // Philoul Others fields below found in API v3 doc + // @SerializedName("app") val app : String, // TODO required ? Application or system in which the record was entered by human or device for the first time. + @SerializedName("utcOffset") val utcOffset: Long?, // Local UTC offset (timezone) of the event in minutes. This field can be set either directly by the client (in the incoming document) or it is + // automatically parsed from the date field. + @SerializedName("subject") val subject: String?, // Name of the security subject (within Nightscout scope) which has created the document. This field is automatically set by the server from the passed token or JWT. + @SerializedName("modifiedBy") val modifiedBy: String?, // Name of the security subject (within Nightscout scope) which has patched or deleted the document for the last time. This field is automatically set by the server. + @SerializedName("isValid") val isValid: Boolean?, // A flag set by the server only for deleted documents. This field appears only within history operation and for documents which were deleted by API v3 (and they always have a false value) + @SerializedName("isReadOnly") val isReadOnly: Boolean?, // A flag set by client that locks the document from any changes. Every document marked with isReadOnly=true is forever immutable and cannot even be deleted. + @SerializedName("noise") val noise: Int?, // 0 or 1 found in the export, I don't know if other values possible ? + @SerializedName("filtered") val filtered: Double?, // The raw filtered value directly from CGM transmitter. (only available for sgv types) + @SerializedName("unfiltered") val unfiltered: Double?, // The raw unfiltered value directly from CGM transmitter. (only available for sgv types) + @SerializedName("units") val units: String?, // The units for the glucose value, mg/dl or mmol/l. It is strongly recommended to fill in this field. +) diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteStatusResponse.kt b/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteStatusResponse.kt new file mode 100644 index 0000000000..15055f9051 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteStatusResponse.kt @@ -0,0 +1,47 @@ +package info.nightscout.sdk.remotemodel + +import com.google.gson.annotations.SerializedName + +internal data class NSResponse(val result: T?) + +internal data class RemoteStatusResponse( + @SerializedName("version") val version: String, + @SerializedName("apiVersion") val apiVersion: String, + @SerializedName("srvDate") val srvDate: Long, + @SerializedName("storage") val storage: RemoteStorage, + @SerializedName("apiPermissions") val apiPermissions: RemoteApiPermissions +) + +internal data class RemoteStorage( + @SerializedName("storage") val storage: String, + @SerializedName("version") val version: String +) + +internal data class RemoteApiPermissions( + @SerializedName("devicestatus") val deviceStatus: RemoteApiPermission, + @SerializedName("entries") val entries: RemoteApiPermission, + @SerializedName("food") val food: RemoteApiPermission, + @SerializedName("profile") val profile: RemoteApiPermission, + @SerializedName("settings") val settings: RemoteApiPermission, + @SerializedName("treatments") val treatments: RemoteApiPermission +) + +internal typealias RemoteApiPermission = String + +internal val RemoteApiPermission.create: Boolean + get() = this.contains('c') + +internal val RemoteApiPermission.read: Boolean + get() = this.contains('r') + +internal val RemoteApiPermission.update: Boolean + get() = this.contains('u') + +internal val RemoteApiPermission.delete: Boolean + get() = this.contains('d') + +internal val RemoteApiPermission.readCreate: Boolean + get() = this.read && this.create + +internal val RemoteApiPermission.full: Boolean + get() = this.create && this.read && this.update && this.delete diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteTreatment.kt b/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteTreatment.kt new file mode 100644 index 0000000000..80929a2133 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteTreatment.kt @@ -0,0 +1,97 @@ +package info.nightscout.sdk.remotemodel + +import com.google.gson.Gson +import com.google.gson.annotations.SerializedName +import info.nightscout.sdk.localmodel.treatment.EventType +import org.joda.time.DateTime +import org.joda.time.format.ISODateTimeFormat +import org.json.JSONObject + +/* +* Depending on the type, different other fields are present. +* Those technically need to be optional. +* +* On upload a sanity check still needs to be done to verify that all mandatory fields for that type are there. +* +* TODO: Find out all types with their optional and mandatory fields +* +* */ +internal data class RemoteTreatment( + @SerializedName("identifier") val identifier: String, // string Main addressing, required field that identifies document in the collection. The client should not create the identifier, the server automatically assigns it when the document is inserted. + @SerializedName("date") val date: Long?, // integer($int64) or string required timestamp when the record or event occurred, you can choose from three input formats Unix epoch in milliseconds (1525383610088), Unix epoch in seconds (1525383610), ISO 8601 with optional timezone ('2018-05-03T21:40:10.088Z' or '2018-05-03T23:40:10.088+02:00') + @SerializedName("mills") val mills: Long?, // integer($int64) or string required timestamp when the record or event occurred, you can choose from three input formats Unix + @SerializedName("timestamp") val timestamp: Long?, // integer($int64) or string required timestamp when the record or event occurred, you can choose from three input formats Unix epoch in milliseconds (1525383610088), Unix epoch in seconds (1525383610), ISO 8601 with optional timezone ('2018-05-03T21:40:10.088Z' or '2018-05-03T23:40:10.088+02:00') + @SerializedName("created_at") val created_at: String, // integer($int64) or string timestamp on previous version of api, in my examples, a lot of treatments don't have date, only created_at, some of them with string others with long... + @SerializedName("utcOffset") val utcOffset: Long?, // integer Local UTC offset (timezone) of the event in minutes. This field can be set either directly by the client (in the incoming + // document) or it is automatically parsed from the date field. + // @SerializedName("app") val app : String, // TODO required ? Application or system in which the record was entered by human or device for the first time. + @SerializedName("device") val device: String?, // string The device from which the data originated (including serial number of the device, if it is relevant and safe). + @SerializedName("srvCreated") val srvCreated: Long, // integer($int64) example: 1525383610088 The server's timestamp of document insertion into the database (Unix epoch in ms). This field appears only for documents which were inserted by API v3. + @SerializedName("subject") val subject: String?, // string Name of the security subject (within Nightscout scope) which has created the document. This field is automatically set by the server from the passed token or JWT. + @SerializedName("srvModified") val srvModified: Long, // integer($int64) example: 1525383610088 The server's timestamp of the last document modification in the database (Unix epoch in ms). This field appears only for documents which were somehow modified by API v3 (inserted, updated or deleted). + @SerializedName("modifiedBy") val modifiedBy: String?, // string Name of the security subject (within Nightscout scope) which has patched or deleted the document for the last time. This field is automatically set by the server. + @SerializedName("isValid") val isValid: Boolean?, // boolean A flag set by the server only for deleted documents. This field appears only within history operation and for documents which were deleted by API v3 (and they always have a false value) + @SerializedName("isReadOnly") val isReadOnly: Boolean?, // boolean A flag set by client that locks the document from any changes. Every document marked with isReadOnly=true is forever immutable and cannot even be deleted. + @SerializedName("eventType") val eventType: EventType, // string "BG Check", "Snack Bolus", "Meal Bolus", "Correction Bolus", "Carb Correction", "Combo Bolus", "Announcement", "Note", "Question", "Exercise", "Site Change", "Sensor Start", "Sensor Change", "Pump Battery Change", "Insulin Change", "Temp Basal", "Profile Switch", "D.A.D. Alert", "Temporary Target", "OpenAPS Offline", "Bolus Wizard" + @SerializedName("glucose") val glucose: Double?, // double Current glucose + @SerializedName("glucoseType") val glucoseType: String?, // string example: "Sensor", "Finger", "Manual" + @SerializedName("units") val units: String?, // string The units for the glucose value, mg/dl or mmol/l. It is strongly recommended to fill in this field. + @SerializedName("carbs") val carbs: Double?, // number... Amount of carbs given. + @SerializedName("protein") val protein: Int?, // number... Amount of protein given. + @SerializedName("fat") val fat: Int?, // number... Amount of fat given. + @SerializedName("insulin") val insulin: Double?, // number... Amount of insulin, if any. + @SerializedName("duration") val duration: Long?, // number... Duration in minutes. + @SerializedName("durationInMilliseconds") val durationInMilliseconds: Long?, // number... Duration in milliseconds. + @SerializedName("preBolus") val preBolus: Int?, // number... How many minutes the bolus was given before the meal started. + @SerializedName("splitNow") val splitNow: Int?, // number... Immediate part of combo bolus (in percent). + @SerializedName("splitExt") val splitExt: Int?, // number... Extended part of combo bolus (in percent). + @SerializedName("percent") val percent: Double?, // number... Eventual basal change in percent. + @SerializedName("absolute") val absolute: Double?, // number... Eventual basal change in absolute value (insulin units per hour). + @SerializedName("targetTop") val targetTop: Double?, // number... Top limit of temporary target. + @SerializedName("targetBottom") val targetBottom: Double?, // number... Bottom limit of temporary target. + @SerializedName("profile") val profile: String?, // string Name of the profile to which the pump has been switched. + @SerializedName("reason") val reason: String?, // string For example the reason why the profile has been switched or why the temporary target has been set. + @SerializedName("notes") val notes: String?, // string Description/notes of treatment. + @SerializedName("enteredBy") val enteredBy: String?, // string Who entered the treatment. + + @SerializedName("endId") val endId: Long?, // long id of record which ended this + @SerializedName("pumpId") val pumpId: Long?, // long or "Meal Bolus", "Correction Bolus", "Combo Bolus" ex 4102 not sure if long or int + @SerializedName("pumpType") val pumpType: String?, // string "Meal Bolus", "Correction Bolus", "Combo Bolus" ex "ACCU_CHEK_INSIGHT_BLUETOOTH", + @SerializedName("pumpSerial") val pumpSerial: String?, // string "Meal Bolus", "Correction Bolus", "Combo Bolus" "33013206", + + // other fields found in examples but not in documentation + @SerializedName("profileJson") val profileJson: String?, // string "Profile Switch" ex json toString "{\"units\":\"mg\\/dl\",\"dia\":5,\"timezone\":\"Africa\\/Cairo\", + // \"sens\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":60},{\"time\":\"07:00\",\"timeAsSeconds\":25200,\"value\":60},{\"time\":\"08:00\",\"timeAsSeconds\":28800,\"value\":61.33333333333333},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":65.33333333333333},{\"time\":\"10:00\",\"timeAsSeconds\":36000,\"value\":69.33333333333333},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":73.33333333333333},{\"time\":\"13:00\",\"timeAsSeconds\":46800,\"value\":72},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":68},{\"time\":\"15:00\",\"timeAsSeconds\":54000,\"value\":65.33333333333333},{\"time\":\"16:00\",\"timeAsSeconds\":57600,\"value\":65.33333333333333}],\"carbratio\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":5.7333333333333325},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":7.333333333333333},{\"time\":\"16:00\",\"timeAsSeconds\":57600,\"value\":6.666666666666666}],\"basal\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":0.5249999999999999},{\"time\":\"01:00\",\"timeAsSeconds\":3600,\"value\":0.585},{\"time\":\"02:00\",\"timeAsSeconds\":7200,\"value\":0.6375},{\"time\":\"03:00\",\"timeAsSeconds\":10800,\"value\":0.5625},{\"time\":\"04:00\",\"timeAsSeconds\":14400,\"value\":0.4575},{\"time\":\"05:00\",\"timeAsSeconds\":18000,\"value\":0.5175},{\"time\":\"06:00\",\"timeAsSeconds\":21600,\"value\":0.48},{\"time\":\"07:00\",\"timeAsSeconds\":25200,\"value\":0.51},{\"time\":\"08:00\",\"timeAsSeconds\":28800,\"value\":0.48750000000000004},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":0.48},{\"time\":\"10:00\",\"timeAsSeconds\":36000,\"value\":0.48750000000000004},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":0.5025000000000001},{\"time\":\"12:00\",\"timeAsSeconds\":43200,\"value\":0.5549999999999999},{\"time\":\"13:00\",\"timeAsSeconds\":46800,\"value\":0.5700000000000001},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":0.5700000000000001},{\"time\":\"15:00\",\"timeAsSeconds\":54000,\"value\":0.5775},{\"time\":\"16:00\",\"timeAsSeconds\":57600,\"value\":0.51},{\"time\":\"17:00\",\"timeAsSeconds\":61200,\"value\":0.54},{\"time\":\"18:00\",\"timeAsSeconds\":64800,\"value\":0.48750000000000004},{\"time\":\"19:00\",\"timeAsSeconds\":68400,\"value\":0.5249999999999999},{\"time\":\"20:00\",\"timeAsSeconds\":72000,\"value\":0.46499999999999997},{\"time\":\"21:00\",\"timeAsSeconds\":75600,\"value\":0.46499999999999997},{\"time\":\"22:00\",\"timeAsSeconds\":79200,\"value\":0.43499999999999994},{\"time\":\"23:00\",\"timeAsSeconds\":82800,\"value\":0.41250000000000003}],\"target_low\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":100},{\"time\":\"06:00\",\"timeAsSeconds\":21600,\"value\":90},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":100},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":90},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":100},{\"time\":\"18:00\",\"timeAsSeconds\":64800,\"value\":90},{\"time\":\"21:00\",\"timeAsSeconds\":75600,\"value\":100}],\"target_high\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":100},{\"time\":\"06:00\",\"timeAsSeconds\":21600,\"value\":90},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":100},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":90},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":100},{\"time\":\"18:00\",\"timeAsSeconds\":64800,\"value\":90},{\"time\":\"21:00\",\"timeAsSeconds\":75600,\"value\":100}]}", + @SerializedName("originalProfileName") val originalProfileName: String?, // string "Effective Profile Switch" + @SerializedName("originalCustomizedName") val originalCustomizedName: String?, // string "Effective Profile Switch" + @SerializedName("originalTimeshift") val originalTimeshift: Long?, // long "Effective Profile Switch" + @SerializedName("originalPercentage") val originalPercentage: Int?, // int "Effective Profile Switch" + @SerializedName("originalDuration") val originalDuration: Long?, // long "Effective Profile Switch" + @SerializedName("originalEnd") val originalEnd: Long?, // long "Effective Profile Switch" + + @SerializedName("bolusCalculatorResult") val bolusCalculatorResult: String?, // string "Bolus Wizard" json toString ex "bolusCalculatorResult": "{\"basalIOB\":-0.247,\"bolusIOB\":-1.837,\"carbs\":45.0,\"carbsInsulin\":9.0,\"cob\":0.0,\"cobInsulin\":0.0,\"dateCreated\":1626202788810,\"glucoseDifference\":44.0,\"glucoseInsulin\":0.8979591836734694,\"glucoseTrend\":5.5,\"glucoseValue\":134.0,\"ic\":5.0,\"id\":331,\"interfaceIDs_backing\":{\"nightscoutId\":\"60ede2a4c574da0004a3869d\"},\"isValid\":true,\"isf\":49.0,\"note\":\"\",\"otherCorrection\":0.0,\"percentageCorrection\":90,\"profileName\":\"Tuned 13/01 90%Lyum\",\"superbolusInsulin\":0.0,\"targetBGHigh\":90.0,\"targetBGLow\":90.0,\"timestamp\":1626202783325,\"totalInsulin\":7.34,\"trendInsulin\":0.336734693877551,\"utcOffset\":7200000,\"version\":1,\"wasBasalIOBUsed\":true,\"wasBolusIOBUsed\":true,\"wasCOBUsed\":true,\"wasGlucoseUsed\":true,\"wasSuperbolusUsed\":false,\"wasTempTargetUsed\":false,\"wasTrendUsed\":true,\"wereCarbsUsed\":false}", + @SerializedName("type") val type: String?, // string "Meal Bolus", "Correction Bolus", "Combo Bolus", "Temp Basal" type of bolus "NORMAL", "SMB", "FAKE_EXTENDED" + @SerializedName("isSMB") val isSMB: Boolean, // boolean "Meal Bolus", "Correction Bolus", "Combo Bolus" + @SerializedName("enteredinsulin") val enteredinsulin: Double?, // number... "Combo Bolus" insulin is missing only enteredinsulin field found + @SerializedName("relative") val relative: Double?, // number... "Combo Bolus", "extendedEmulated" (not in doc see below) + @SerializedName("isEmulatingTempBasal") val isEmulatingTempBasal: Boolean, // boolean "Combo Bolus", "extendedEmulated" (not in doc see below) + @SerializedName("isAnnouncement") val isAnnouncement: Boolean, // boolean "Announcement" + @SerializedName("rate") val rate: Double?, // Double "Temp Basal" absolute rate (could be calculated with percent and profile information...) + @SerializedName("extendedEmulated") val extendedEmulated: RemoteTreatment?, // Gson of emulated EB + @SerializedName("timeshift") val timeshift: Long, // integer "Profile Switch" + @SerializedName("percentage") val percentage: Int?, // integer "Profile Switch" +) { + + fun timestamp(): Long { + return date ?: mills ?: timestamp ?: fromISODateString(created_at) + } + + private fun fromISODateString(isoDateString: String): Long = + try { + val parser = ISODateTimeFormat.dateTimeParser() + val dateTime = DateTime.parse(isoDateString, parser) + dateTime.toDate().time + } catch (e: Exception) { + 0L + } +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/examples.json b/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/examples.json new file mode 100644 index 0000000000..87b19cad8b --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/examples.json @@ -0,0 +1,490 @@ +// Entry +{ + "device": "xDrip-Follower", + "date": 1549414398005, + "dateString": "2019-02-06T01:53:18.005+0100", + "sgv": 98, + "delta": -1.132, + "direction": "Flat", + "type": "sgv", + "filtered": 90336, + "unfiltered": 89712, + "rssi": 100, + "noise": 1, + "sysTime": "2019-02-06T01:53:18.005+0100", + "identifier": "5c5a3007e0196f4d3d9aeafc", + "srvModified": 1549414398005, + "srvCreated": 1549414398005 +}, + +// G6 AAPS +{ + "_id": "60bace9f51e8150004f0973a", + "device": "AndroidAPS-DexcomG6", + "date": 1622855221000, + "dateString": "2021-06-05T01:07:01.000Z", + "isValid": true, + "sgv": 76, + "direction": "Flat", // DoubleDown, SingleDown, FortyFiveDown, Flat, FortyFiveUp, SingleUp, DoubleUp + "type": "sgv", + "created_at": "2021-06-05T01:08:47.234Z" +}, +// G6 DEXCOM APP Share +{ + "_id": "60cd4e7d5bcdeb30e43a248d", + "sgv": 90, + "date": 1624067551000, + "dateString": "2021-06-19T01:52:31.000Z", + "trend": 4, // 7 , 6 , 5 , 4 , 3 , 2 , 1 + "direction": "Flat", // DoubleDown, SingleDown, FortyFiveDown, Flat, FortyFiveUp, SingleUp, DoubleUp + "device": "share2", + "type": "sgv", + "utcOffset": 0, + "sysTime": "2021-06-19T01:52:31.000Z" +}, +// FSL1 xDrip +{ + "device": "AndroidAPS", + "date": 1588557121000, + "dateString": "2020-05-04T01:52:01Z", + "sgv": 76, + "direction": "Flat", // DoubleDown, SingleDown, FortyFiveDown, Flat, FortyFiveUp, SingleUp, DoubleUp + "type": "sgv", + "systime": "2020-05-04T01:52:01Z", + "utcOffset": 120 +}, +// LimiTTer xDrip +{ + "_id": "5ed06c9a0ea4dcb70fac6cc7", + "device": "xDrip-LimiTTer", + "date": 1590717591357, + "dateString": "2020-05-29T01:59:51.357Z", + "sgv": 114, + "delta": -2.942, + "direction": "Flat", + "type": "sgv", + "filtered": 127411.75515, + "unfiltered": 127411.75515, + "rssi": 100, + "noise": 1, + "sysTime": "2020-05-29T01:59:51.357Z", + "utcOffset": 120 +}, + +// API v3 requests for treatments +{ + "eventType": "BG Check", + "created_at": 1616966443000, + "units": "mg/dl", + "glucose": 57, + "NSCLIENT_ID": "1616966443000", + "identifier": "6060f32e9b9c5900045c858b", + "srvModified": 1616966443000, + "srvCreated": 1616966443000 +}, +{ + "eventType": "BG Check", + "created_at": 1617365936000, + "enteredBy": "AndroidAPS", + "units": "mg/dl", + "notes": "Coucou", + "glucose": 94, + "glucoseType": "Finger", + "identifier": "606727c058f71500041e1ed3", + "srvModified": 1617365936000, + "srvCreated": 1617365936000 +}, +{ + "eventType": "Meal Bolus", + "carbs": 45, + "created_at": "2021-07-13T18:19:43.325Z", + "isValid": true, + "date": 1626200383325, + "identifier": "60ede2a4c574da0004a3869c", + "srvModified": 1626200383325, + "srvCreated": 1626200383325 +}, +{ + "eventType": "Meal Bolus", + "insulin": 8.1, + "created_at": "2021-07-13T11:25:12.664Z", + "date": 1626175512664, + "type": "NORMAL", + "isValid": true, + "isSMB": false, + "pumpId": 4102, + "pumpType": "ACCU_CHEK_INSIGHT_BLUETOOTH", + "pumpSerial": "33013206", + "identifier": "60ed782dc574da0004a38595", + "srvModified": 1626175512664, + "srvCreated": 1626175512664 +}, +{ + "eventType": "Correction Bolus", + "insulin": 0.25, + "created_at": "2021-07-13T20:44:14.441Z", + "date": 1626209054441, + "type": "SMB", + "isValid": true, + "isSMB": true, + "pumpId": 4148, + "pumpType": "ACCU_CHEK_INSIGHT_BLUETOOTH", + "pumpSerial": "33013206", + "identifier": "60edfb34c574da0004a386d4", + "srvModified": 1626209054441, + "srvCreated": 1626209054441 +},{ + "eventType": "Carb Correction", + "carbs": 5, + "created_at": "2021-06-17T09:00:34.000Z", + "isValid": true, + "date": 1623920434000, + "identifier": "60cb2f351a94d4000483b692", + "srvModified": 1623920434000, + "srvCreated": 1623920434000 +}, +{ +"created_at": "2021-05-28T19:46:43.851Z", +"enteredBy": "openaps://AndroidAPS", +"eventType": "Combo Bolus", +"duration": 5, +"splitNow": 0, +"splitExt": 100, +"enteredinsulin": 0.7890262726962469, +"relative": 8.893749414356174, +"isValid": true, +"isEmulatingTempBasal": false, +"pumpId": 4, +"pumpType": "ACCU_CHEK_INSIGHT_BLUETOOTH", +"pumpSerial": "33010032", +"identifier": "60b148b419cf4300040b0195", +"srvModified": 1622231203851, +"srvCreated": 1622231203851 +}, +{ + "eventType": "Announcement", + "created_at": 1617350431592, + "enteredBy": "AndroidAPS", + "units": "mg/dl", + "notes": "5g de glucides requis dans 40 min.", + "isAnnouncement": true, + "identifier": "6066cf2508a6ed0004b4ed44", + "srvModified": 1617350431592, + "srvCreated": 1617350431592 +}, +{ + "eventType": "Note", + "created_at": 1617023462485, + "units": "mg/dl", + "notes": "AndroidAPS started - Logicom Le Hola FR", + "identifier": "6061d20b17619800047216b2", + "srvModified": 1617023462485, + "srvCreated": 1617023462485 +}, +{ + "eventType": "Exercise", + "created_at": 1617373066000, + "enteredBy": "AndroidAPS", + "units": "mg/dl", + "duration": 20, + "notes": "ten tab", + "identifier": "606727a658f71500041e1ed2", + "srvModified": 1617373066000, + "srvCreated": 1617373066000 +}, +{ + "eventType": "Exercise", + "isValid": true, + "created_at": "2021-07-09T18:15:22.000Z", + "enteredBy": "AndroidAPS", + "units": "mg/dl", + "duration": 105, + "notes": "🏓", + "identifier": "60e8b223b98ea2000472cbb3", + "srvModified": 1625854522000, + "srvCreated": 1625854522000 +}, +{ + "eventType": "Site Change", + "created_at": 1616312250000, + "units": "mg/dl", + "notes": "", + "NSCLIENT_ID": "1616312250000", + "identifier": "6056f7c1bc2dc60004e75499", + "srvModified": 1616312250000, + "srvCreated": 1616312250000 +}, +{ + "eventType": "Sensor Change", + "created_at": 1617373059000, + "enteredBy": "AndroidAPS", + "units": "mg/dl", + "identifier": "6067278d58f71500041e1ed1", + "srvModified": 1617373059000, + "srvCreated": 1617373059000 +}, +{ + "enteredBy": "AndroidAPS-DexcomG6", + "created_at": 1617799461000, + "eventType": "Sensor Change", + "NSCLIENT_ID": "1617961262771", + "identifier": "60702190403172000451e5dc", + "srvModified": 1617799461000, + "srvCreated": 1617799461000 +}, +{ + "eventType": "Pump Battery Change", + "created_at": 1616517575000, + "enteredBy": "AndroidAPS", + "units": "mg/dl", + "notes": "à peu près...", + "NSCLIENT_ID": "1616517575000", + "identifier": "605cbce3f9ed3b0004694ee8", + "srvModified": 1616517575000, + "srvCreated": 1616517575000 +}, +{ + "created_at": 1617576811000, + "eventType": "Pump Battery Change", + "NSCLIENT_ID": "1617577097394", + "glucoseType": "Manual", + "isValid": true, + "units": "mg/dl", + "identifier": "606a448d7c31f00004bb47ac", + "srvModified": 1617576811000, + "srvCreated": 1617576811000 +}, +{ + "eventType": "Insulin Change", + "created_at": 1616342559000, + "units": "mg/dl", + "notes": "Ajout manuel pour UE", + "NSCLIENT_ID": "1616342559000", + "identifier": "60576e6b5a34f900043e25f6", + "srvModified": 1616342559000, + "srvCreated": 1616342559000 +}, +{ + "created_at": "2021-07-13T20:44:12.891Z", + "enteredBy": "openaps://AndroidAPS", + "eventType": "Temp Basal", + "isValid": true, + "duration": 60, + "rate": 0, + "type": "NORMAL", + "absolute": 0, + "pumpId": 284835, + "pumpType": "ACCU_CHEK_INSIGHT_BLUETOOTH", + "pumpSerial": "33013206", + "identifier": "60edfb34c574da0004a386d3", + "srvModified": 1626209052891, + "srvCreated": 1626209052891 +}, +{ + "created_at": "2021-07-13T20:40:29.896Z", + "enteredBy": "openaps://AndroidAPS", + "eventType": "Temp Basal", + "isValid": true, + "duration": 3, + "rate": 2.4391549295774646, + "type": "FAKE_EXTENDED", + "absolute": 2.4391549295774646, + "pumpId": 4147, + "pumpType": "ACCU_CHEK_INSIGHT_BLUETOOTH", + "pumpSerial": "33013206", + "extendedEmulated": { + "created_at": "2021-07-13T20:40:29.896Z", + "enteredBy": "openaps://AndroidAPS", + "eventType": "Combo Bolus", + "duration": 3, + "splitNow": 0, + "splitExt": 100, + "enteredinsulin": 0.11, + "relative": 1.8591549295774648, + "isValid": true, + "isEmulatingTempBasal": true, + "pumpId": 4147, + "pumpType": "ACCU_CHEK_INSIGHT_BLUETOOTH", + "pumpSerial": "33013206" + }, + "identifier": "60edfa51c574da0004a386d0", + "srvModified": 1626208829896, + "srvCreated": 1626208829896 +}, +{ + "eventType": "OpenAPS Offline", + "created_at": 1616391934628, + "enteredBy": "openaps://AndroidAPS", + "units": "mg/dl", + "duration": 15, + "NSCLIENT_ID": "1616391934628", + "identifier": "60582f005a34f900043e2845", + "srvModified": 1616391934628, + "srvCreated": 1616391934628 +}, +{ + "created_at": "2021-06-26T13:36:47.000Z", + "enteredBy": "openaps://AndroidAPS", + "isValid": true, + "eventType": "Profile Switch", + "duration": 0, + "profile": "Tuned 13/01 90%Lyum", + "profileJson": "{\"units\":\"mg\\/dl\",\"dia\":5,\"timezone\":\"Africa\\/Cairo\",\"sens\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":45},{\"time\":\"07:00\",\"timeAsSeconds\":25200,\"value\":45},{\"time\":\"08:00\",\"timeAsSeconds\":28800,\"value\":46},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":49},{\"time\":\"10:00\",\"timeAsSeconds\":36000,\"value\":52},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":55},{\"time\":\"13:00\",\"timeAsSeconds\":46800,\"value\":54},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":51},{\"time\":\"15:00\",\"timeAsSeconds\":54000,\"value\":49},{\"time\":\"16:00\",\"timeAsSeconds\":57600,\"value\":49}],\"carbratio\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":4.3},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":5.5},{\"time\":\"16:00\",\"timeAsSeconds\":57600,\"value\":5}],\"basal\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":0.7},{\"time\":\"01:00\",\"timeAsSeconds\":3600,\"value\":0.78},{\"time\":\"02:00\",\"timeAsSeconds\":7200,\"value\":0.85},{\"time\":\"03:00\",\"timeAsSeconds\":10800,\"value\":0.75},{\"time\":\"04:00\",\"timeAsSeconds\":14400,\"value\":0.61},{\"time\":\"05:00\",\"timeAsSeconds\":18000,\"value\":0.69},{\"time\":\"06:00\",\"timeAsSeconds\":21600,\"value\":0.64},{\"time\":\"07:00\",\"timeAsSeconds\":25200,\"value\":0.68},{\"time\":\"08:00\",\"timeAsSeconds\":28800,\"value\":0.65},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":0.64},{\"time\":\"10:00\",\"timeAsSeconds\":36000,\"value\":0.65},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":0.67},{\"time\":\"12:00\",\"timeAsSeconds\":43200,\"value\":0.74},{\"time\":\"13:00\",\"timeAsSeconds\":46800,\"value\":0.76},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":0.76},{\"time\":\"15:00\",\"timeAsSeconds\":54000,\"value\":0.77},{\"time\":\"16:00\",\"timeAsSeconds\":57600,\"value\":0.68},{\"time\":\"17:00\",\"timeAsSeconds\":61200,\"value\":0.72},{\"time\":\"18:00\",\"timeAsSeconds\":64800,\"value\":0.65},{\"time\":\"19:00\",\"timeAsSeconds\":68400,\"value\":0.7},{\"time\":\"20:00\",\"timeAsSeconds\":72000,\"value\":0.62},{\"time\":\"21:00\",\"timeAsSeconds\":75600,\"value\":0.62},{\"time\":\"22:00\",\"timeAsSeconds\":79200,\"value\":0.58},{\"time\":\"23:00\",\"timeAsSeconds\":82800,\"value\":0.55}],\"target_low\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":100},{\"time\":\"06:00\",\"timeAsSeconds\":21600,\"value\":90},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":100},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":90},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":100},{\"time\":\"18:00\",\"timeAsSeconds\":64800,\"value\":90},{\"time\":\"21:00\",\"timeAsSeconds\":75600,\"value\":100}],\"target_high\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":100},{\"time\":\"06:00\",\"timeAsSeconds\":21600,\"value\":90},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":100},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":90},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":100},{\"time\":\"18:00\",\"timeAsSeconds\":64800,\"value\":90},{\"time\":\"21:00\",\"timeAsSeconds\":75600,\"value\":100}]}", + "timeshift": 0, + "percentage": 100, + "identifier": "60d72d80aec46a0004f95163", + "srvModified": 1624714607000, + "srvCreated": 1624714607000 +}, +{ + "created_at": "2021-06-13T07:20:33.000Z", + "enteredBy": "openaps://AndroidAPS", + "isValid": true, + "eventType": "Profile Switch", + "duration": 150, + "profile": "Tuned 13/01 90%Lyum(75%)", + "profileJson": "{\"units\":\"mg\\/dl\",\"dia\":5,\"timezone\":\"Africa\\/Cairo\",\"sens\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":60},{\"time\":\"07:00\",\"timeAsSeconds\":25200,\"value\":60},{\"time\":\"08:00\",\"timeAsSeconds\":28800,\"value\":61.33333333333333},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":65.33333333333333},{\"time\":\"10:00\",\"timeAsSeconds\":36000,\"value\":69.33333333333333},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":73.33333333333333},{\"time\":\"13:00\",\"timeAsSeconds\":46800,\"value\":72},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":68},{\"time\":\"15:00\",\"timeAsSeconds\":54000,\"value\":65.33333333333333},{\"time\":\"16:00\",\"timeAsSeconds\":57600,\"value\":65.33333333333333}],\"carbratio\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":5.7333333333333325},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":7.333333333333333},{\"time\":\"16:00\",\"timeAsSeconds\":57600,\"value\":6.666666666666666}],\"basal\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":0.5249999999999999},{\"time\":\"01:00\",\"timeAsSeconds\":3600,\"value\":0.585},{\"time\":\"02:00\",\"timeAsSeconds\":7200,\"value\":0.6375},{\"time\":\"03:00\",\"timeAsSeconds\":10800,\"value\":0.5625},{\"time\":\"04:00\",\"timeAsSeconds\":14400,\"value\":0.4575},{\"time\":\"05:00\",\"timeAsSeconds\":18000,\"value\":0.5175},{\"time\":\"06:00\",\"timeAsSeconds\":21600,\"value\":0.48},{\"time\":\"07:00\",\"timeAsSeconds\":25200,\"value\":0.51},{\"time\":\"08:00\",\"timeAsSeconds\":28800,\"value\":0.48750000000000004},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":0.48},{\"time\":\"10:00\",\"timeAsSeconds\":36000,\"value\":0.48750000000000004},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":0.5025000000000001},{\"time\":\"12:00\",\"timeAsSeconds\":43200,\"value\":0.5549999999999999},{\"time\":\"13:00\",\"timeAsSeconds\":46800,\"value\":0.5700000000000001},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":0.5700000000000001},{\"time\":\"15:00\",\"timeAsSeconds\":54000,\"value\":0.5775},{\"time\":\"16:00\",\"timeAsSeconds\":57600,\"value\":0.51},{\"time\":\"17:00\",\"timeAsSeconds\":61200,\"value\":0.54},{\"time\":\"18:00\",\"timeAsSeconds\":64800,\"value\":0.48750000000000004},{\"time\":\"19:00\",\"timeAsSeconds\":68400,\"value\":0.5249999999999999},{\"time\":\"20:00\",\"timeAsSeconds\":72000,\"value\":0.46499999999999997},{\"time\":\"21:00\",\"timeAsSeconds\":75600,\"value\":0.46499999999999997},{\"time\":\"22:00\",\"timeAsSeconds\":79200,\"value\":0.43499999999999994},{\"time\":\"23:00\",\"timeAsSeconds\":82800,\"value\":0.41250000000000003}],\"target_low\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":100},{\"time\":\"06:00\",\"timeAsSeconds\":21600,\"value\":90},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":100},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":90},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":100},{\"time\":\"18:00\",\"timeAsSeconds\":64800,\"value\":90},{\"time\":\"21:00\",\"timeAsSeconds\":75600,\"value\":100}],\"target_high\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":100},{\"time\":\"06:00\",\"timeAsSeconds\":21600,\"value\":90},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":100},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":90},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":100},{\"time\":\"18:00\",\"timeAsSeconds\":64800,\"value\":90},{\"time\":\"21:00\",\"timeAsSeconds\":75600,\"value\":100}]}", + "timeshift": 0, + "percentage": 100, + "identifier": "60c5b1f41b3715000420af27", + "srvModified": 1623568833000, + "srvCreated": 1623568833000 +}, +{ + "eventType": "Temporary Target", + "duration": 60, + "isValid": true, + "created_at": "2021-07-10T05:04:11.566Z", + "enteredBy": "AndroidAPS", + "reason": "Automation", + "targetBottom": 110, + "targetTop": 110, + "units": "mg/dl", + "identifier": "60e92a644fc2eb00045ece1b", + "srvModified": 1625893451566, + "srvCreated": 1625893451566 +}, +{ + "eventType": "Temporary Target", + "duration": 120, + "isValid": true, + "created_at": "2021-07-09T20:30:21.627Z", + "enteredBy": "AndroidAPS", + "reason": "Hypo", + "targetBottom": 140, + "targetTop": 140, + "units": "mg/dl", + "identifier": "60e8b1f2b98ea2000472cbb1", + "srvModified": 1625862621627, + "srvCreated": 1625862621627 +}, +{ + "eventType": "Bolus Wizard", + "created_at": "2021-07-13T18:59:43.325Z", + "isValid": true, + "bolusCalculatorResult": "{\"basalIOB\":-0.247,\"bolusIOB\":-1.837,\"carbs\":45.0,\"carbsInsulin\":9.0,\"cob\":0.0,\"cobInsulin\":0.0,\"dateCreated\":1626202788810,\"glucoseDifference\":44.0,\"glucoseInsulin\":0.8979591836734694,\"glucoseTrend\":5.5,\"glucoseValue\":134.0,\"ic\":5.0,\"id\":331,\"interfaceIDs_backing\":{\"nightscoutId\":\"60ede2a4c574da0004a3869d\"},\"isValid\":true,\"isf\":49.0,\"note\":\"\",\"otherCorrection\":0.0,\"percentageCorrection\":90,\"profileName\":\"Tuned 13/01 90%Lyum\",\"superbolusInsulin\":0.0,\"targetBGHigh\":90.0,\"targetBGLow\":90.0,\"timestamp\":1626202783325,\"totalInsulin\":7.34,\"trendInsulin\":0.336734693877551,\"utcOffset\":7200000,\"version\":1,\"wasBasalIOBUsed\":true,\"wasBolusIOBUsed\":true,\"wasCOBUsed\":true,\"wasGlucoseUsed\":true,\"wasSuperbolusUsed\":false,\"wasTempTargetUsed\":false,\"wasTrendUsed\":true,\"wereCarbsUsed\":false}", + "date": 1626202783325, + "glucose": 134, + "units": "mg/dl", + "notes": "", + "identifier": "60ede2a4c574da0004a3869d", + "srvModified": 1626202783325, + "srvCreated": 1626202783325 +}, + +DEVICE STATUS with configuration +--------------------------------- +{ + "_id": "635abf2069a34517e83768cd", + "created_at": "2022-10-27T17:25:49.730Z", + "device": "openaps://samsung SM-G970F", + "pump": { + "battery": { + "percent": 100 + }, + "status": { + "status": "normal", + "timestamp": "2022-10-27T17:16:11.504Z" + }, + "extended": { + "Version": "3.1.0.3-dev-c-nscv3-8da78d7351-2022.10.25-19:56", + "LastBolus": "10/27/22 18:40", + "LastBolusAmount": 0.35, + "TempBasalAbsoluteRate": 0, + "TempBasalStart": "10/27/22 18:50", + "TempBasalRemaining": 24, + "BaseBasalRate": 1, + "ActiveProfile": "LocalProfile1" + }, + "reservoir": 191, + "clock": "2022-10-27T17:25:49.759Z" + }, + "openaps": { + "suggested": { + "temp": "absolute", + "bg": 72, + "tick": -6, + "eventualBG": 4, + "snoozeBG": 4, + "predBGs": { + "IOB": [72, 61, 51, 42, 39, 39, 39, 39, 39, 39, 39, 39, 39] + }, + "COB": 0, + "IOB": 0.052, + "reason": "COB: 0, Dev: -66, BGI: -0.88, ISF: 2.0, Target: 6.0; BG 4.0<4.4, but 25m left and 0 ~ req 0U/hr: no action required", + "timestamp": "2022-10-27T17:25:49.726Z" + }, + "iob": { + "iob": 0.052, + "basaliob": 0.052, + "activity": 0.0049, + "time": "2022-10-27T17:25:49.726Z" + } + }, + "uploaderBattery": 100, + "configuration": { + "insulin": 5, + "insulinConfiguration": {}, + "sensitivity": 2, + "sensitivityConfiguration": { + "openapsama_min_5m_carbimpact": 10, + "absorption_cutoff": 4, + "autosens_max": 1.2, + "autosens_min": 0.7 + }, + "overviewConfiguration": { + "units": "mmol", + "eatingsoon_duration": 0, + "eatingsoon_target": 0, + "activity_duration": 0, + "activity_target": 0, + "hypo_duration": 0, + "hypo_target": 0, + "low_mark": 4, + "high_mark": 0, + "statuslights_cage_warning": 48, + "statuslights_cage_critical": 72, + "statuslights_iage_warning": 72, + "statuslights_iage_critical": 144, + "statuslights_sage_warning": 216, + "statuslights_sage_critical": 240, + "statuslights_sbat_warning": 25, + "statuslights_sbat_critical": 5, + "statuslights_bage_warning": 216, + "statuslights_bage_critical": 240, + "statuslights_res_warning": 80, + "statuslights_res_critical": 10, + "statuslights_bat_warning": 25, + "statuslights_bat_critical": 5, + "boluswizard_percentage": 60 + }, + "safetyConfiguration": { + "age": "teenage", + "treatmentssafety_maxbolus": 4, + "treatmentssafety_maxcarbs": 60 + }, + "pump": "DanaR", + "version": "3.1.0.3-dev-c-nscv3" + }, + "mills": 1666891549730 +} \ No newline at end of file diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/utils/CoroutineUtils.kt b/ns-sdk/src/main/java/info/nightscout/sdk/utils/CoroutineUtils.kt new file mode 100644 index 0000000000..9b6b777f13 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/utils/CoroutineUtils.kt @@ -0,0 +1,22 @@ +package info.nightscout.sdk.utils + +import kotlinx.coroutines.delay +import kotlin.reflect.KClass + +@Suppress("TooGenericExceptionCaught") +internal suspend fun retry( + numberOfRetries: Int, + delayBetweenRetries: Long, + excludedExceptions: List>, + block: suspend () -> T +): T { + repeat(numberOfRetries) { + try { + return block() + } catch (exception: Exception) { + if (exception::class in excludedExceptions) throw exception + } + delay(delayBetweenRetries) + } + return block() +} diff --git a/ns-sdk/src/main/java/info/nightscout/sdk/utils/ListUtils.kt b/ns-sdk/src/main/java/info/nightscout/sdk/utils/ListUtils.kt new file mode 100644 index 0000000000..12e326db36 --- /dev/null +++ b/ns-sdk/src/main/java/info/nightscout/sdk/utils/ListUtils.kt @@ -0,0 +1,4 @@ +package info.nightscout.sdk.utils + +@JvmSynthetic +internal fun List?.toNotNull(): List = this?.filterNotNull() ?: listOf() diff --git a/openhumans/build.gradle b/openhumans/build.gradle index 1d7f6b7f30..87b80dab73 100644 --- a/openhumans/build.gradle +++ b/openhumans/build.gradle @@ -1,11 +1,14 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" android { diff --git a/plugins/build.gradle b/plugins/build.gradle index f75226f9a9..0a538ec21d 100644 --- a/plugins/build.gradle +++ b/plugins/build.gradle @@ -1,12 +1,15 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" android { namespace 'info.nightscout.plugins' diff --git a/plugins/src/main/java/info/nightscout/plugins/general/autotune/AutotuneIob.kt b/plugins/src/main/java/info/nightscout/plugins/general/autotune/AutotuneIob.kt index 484cececce..a64f354b03 100644 --- a/plugins/src/main/java/info/nightscout/plugins/general/autotune/AutotuneIob.kt +++ b/plugins/src/main/java/info/nightscout/plugins/general/autotune/AutotuneIob.kt @@ -1,6 +1,5 @@ package info.nightscout.plugins.general.autotune -import info.nightscout.interfaces.Constants import info.nightscout.androidaps.data.IobTotal import info.nightscout.androidaps.data.LocalInsulin import info.nightscout.androidaps.database.AppRepository @@ -17,14 +16,15 @@ import info.nightscout.androidaps.extensions.toJson import info.nightscout.androidaps.extensions.toTemporaryBasal import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.plugins.sync.nsclient.extensions.toJson import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.interfaces.utils.Round import info.nightscout.androidaps.utils.T +import info.nightscout.interfaces.Constants +import info.nightscout.interfaces.utils.Round import info.nightscout.plugins.R import info.nightscout.plugins.general.autotune.data.ATProfile import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag - import info.nightscout.shared.sharedPreferences.SP import org.json.JSONArray import org.json.JSONObject @@ -41,6 +41,7 @@ open class AutotuneIob @Inject constructor( private val dateUtil: DateUtil, private val autotuneFS: AutotuneFS ) { + private var nsTreatments = ArrayList() private var dia: Double = Constants.defaultDIA var boluses: ArrayList = ArrayList() @@ -58,10 +59,10 @@ open class AutotuneIob @Inject constructor( nsTreatments.clear() meals.clear() boluses.clear() - tempBasals = ArrayList() + tempBasals = ArrayList() if (profileFunction.getProfile(from - range()) == null) return - initializeBgreadings(from, to) + initializeBgReadings(from, to) initializeTreatmentData(from - range(), to) initializeTempBasalData(from - range(), to, tunedProfile) initializeExtendedBolusData(from - range(), to, tunedProfile) @@ -87,14 +88,17 @@ open class AutotuneIob @Inject constructor( boluses = ArrayList(boluses.toList().sortedWith { o1: Bolus, o2: Bolus -> (o2.timestamp - o1.timestamp).toInt() }) } - private fun initializeBgreadings(from: Long, to: Long) { + private fun initializeBgReadings(from: Long, to: Long) { glucose = repository.compatGetBgReadingsDataFromTime(from, to, false).blockingGet() } //nsTreatment is used only for export data, meals is used in AutotunePrep private fun initializeTreatmentData(from: Long, to: Long) { val oldestBgDate = if (glucose.isNotEmpty()) glucose[glucose.size - 1].timestamp else from - aapsLogger.debug(LTag.AUTOTUNE, "Check BG date: BG Size: " + glucose.size + " OldestBG: " + dateUtil.dateAndTimeAndSecondsString(oldestBgDate) + " to: " + dateUtil.dateAndTimeAndSecondsString(to)) + aapsLogger.debug( + LTag.AUTOTUNE, + "Check BG date: BG Size: " + glucose.size + " OldestBG: " + dateUtil.dateAndTimeAndSecondsString(oldestBgDate) + " to: " + dateUtil.dateAndTimeAndSecondsString(to) + ) val tmpCarbs = repository.getCarbsDataFromTimeToTimeExpanded(from, to, false).blockingGet() aapsLogger.debug(LTag.AUTOTUNE, "Nb treatments after query: " + tmpCarbs.size) var nbCarbs = 0 @@ -170,7 +174,7 @@ open class AutotuneIob @Inject constructor( timestamp = newStart, rate = 100.0, duration = previousStart - newStart, - interfaceIDs_backing = InterfaceIDs(nightscoutId = "neutral_" + newStart.toString()), + interfaceIDs_backing = InterfaceIDs(nightscoutId = "neutral_$newStart"), type = TemporaryBasal.Type.NORMAL ) toSplittedTimestampTB(neutralTbr, tunedProfile) @@ -184,7 +188,7 @@ open class AutotuneIob @Inject constructor( timestamp = from, rate = 100.0, duration = previousStart - from, - interfaceIDs_backing = InterfaceIDs(nightscoutId = "neutral_" + from.toString()), + interfaceIDs_backing = InterfaceIDs(nightscoutId = "neutral_$from"), type = TemporaryBasal.Type.NORMAL ) toSplittedTimestampTB(neutralTbr, tunedProfile) @@ -202,7 +206,7 @@ open class AutotuneIob @Inject constructor( val endTimestamp = splittedTimestamp + splittedDuration while (splittedDuration > 0) { if (Profile.milliSecFromMidnight(splittedTimestamp) / cutInMilliSec == Profile.milliSecFromMidnight(endTimestamp) / cutInMilliSec) { - val newtb = TemporaryBasal( + val newTb = TemporaryBasal( isValid = true, isAbsolute = tb.isAbsolute, timestamp = splittedTimestamp, @@ -211,15 +215,15 @@ open class AutotuneIob @Inject constructor( interfaceIDs_backing = tb.interfaceIDs_backing, type = tb.type ) - tempBasals.add(newtb) - nsTreatments.add(NsTreatment(newtb)) + tempBasals.add(newTb) + nsTreatments.add(NsTreatment(newTb)) splittedDuration = 0 - val profile = profileFunction.getProfile(newtb.timestamp) ?:continue - boluses.addAll(convertToBoluses(newtb, profile, tunedProfile.profile)) // - // required for correct iob calculation with oref0 algo + val profile = profileFunction.getProfile(newTb.timestamp) ?: continue + boluses.addAll(convertToBoluses(newTb, profile, tunedProfile.profile)) // + // required for correct iob calculation with oref0 algo } else { val durationFilled = (cutInMilliSec - Profile.milliSecFromMidnight(splittedTimestamp) % cutInMilliSec) - val newtb = TemporaryBasal( + val newTb = TemporaryBasal( isValid = true, isAbsolute = tb.isAbsolute, timestamp = splittedTimestamp, @@ -228,20 +232,19 @@ open class AutotuneIob @Inject constructor( interfaceIDs_backing = tb.interfaceIDs_backing, type = tb.type ) - tempBasals.add(newtb) - nsTreatments.add(NsTreatment(newtb)) + tempBasals.add(newTb) + nsTreatments.add(NsTreatment(newTb)) splittedTimestamp += durationFilled splittedDuration -= durationFilled - val profile = profileFunction.getProfile(newtb.timestamp) ?:continue - boluses.addAll(convertToBoluses(newtb, profile, tunedProfile.profile)) // required for correct iob calculation with oref0 algo + val profile = profileFunction.getProfile(newTb.timestamp) ?: continue + boluses.addAll(convertToBoluses(newTb, profile, tunedProfile.profile)) // required for correct iob calculation with oref0 algo } } } } - open fun getIOB(time: Long, localInsulin: LocalInsulin): IobTotal { - return getCalculationToTimeTreatments(time, localInsulin).round() - } + open fun getIOB(time: Long, localInsulin: LocalInsulin): IobTotal = + getCalculationToTimeTreatments(time, localInsulin).round() private fun getCalculationToTimeTreatments(time: Long, localInsulin: LocalInsulin): IobTotal { val total = IobTotal(time) @@ -252,14 +255,19 @@ open class AutotuneIob @Inject constructor( if (t.timestamp > time || t.timestamp < time - localInsulin.duration) continue val tIOB = t.iobCalc(time, localInsulin) if (detailedLog) - log("iobCalc;${t.interfaceIDs.nightscoutId};$time;${t.timestamp};${tIOB.iobContrib};${tIOB.activityContrib};${dateUtil.dateAndTimeAndSecondsString(time)};${dateUtil.dateAndTimeAndSecondsString(t.timestamp)}") + log( + "iobCalc;${t.interfaceIDs.nightscoutId};$time;${t.timestamp};${tIOB.iobContrib};${tIOB.activityContrib};${dateUtil.dateAndTimeAndSecondsString(time)};${ + dateUtil.dateAndTimeAndSecondsString( + t.timestamp + ) + }" + ) total.iob += tIOB.iobContrib total.activity += tIOB.activityContrib } return total } - private fun convertToBoluses(eb: ExtendedBolus): MutableList { val result: MutableList = ArrayList() val aboutFiveMinIntervals = ceil(eb.duration / 5.0).toInt() @@ -285,11 +293,13 @@ open class AutotuneIob @Inject constructor( val realDuration = tbr.durationInMinutes val basalRate = profile.getBasal(tbr.timestamp) val tunedRate = tunedProfile.getBasal(tbr.timestamp) - val netBasalRate = Round.roundTo(if (tbr.isAbsolute) { - tbr.rate - tunedRate - } else { - tbr.rate / 100.0 * basalRate - tunedRate - }, 0.001) + val netBasalRate = Round.roundTo( + if (tbr.isAbsolute) { + tbr.rate - tunedRate + } else { + tbr.rate / 100.0 * basalRate - tunedRate + }, 0.001 + ) val aboutFiveMinIntervals = ceil(realDuration / 5.0).toInt() val tempBolusSpacing = realDuration / aboutFiveMinIntervals.toDouble() for (j in 0L until aboutFiveMinIntervals) { @@ -311,8 +321,8 @@ open class AutotuneIob @Inject constructor( @Synchronized fun glucoseToJSON(): String { val glucoseJson = JSONArray() - for (bgreading in glucose) - glucoseJson.put(bgreading.toJson(true, dateUtil)) + for (bgReading in glucose) + glucoseJson.put(bgReading.toJson(true, dateUtil)) return glucoseJson.toString(2) } @@ -368,7 +378,7 @@ open class AutotuneIob @Inject constructor( } fun toJson(): JSONObject? { - val cPjson = JSONObject() + val cpJson = JSONObject() return when (eventType) { TherapyEvent.Type.TEMPORARY_BASAL -> temporaryBasal?.let { tbr -> @@ -377,6 +387,7 @@ open class AutotuneIob @Inject constructor( tbr.toJson(true, it, dateUtil) } } + TherapyEvent.Type.COMBO_BOLUS -> extendedBolus?.let { ebr -> val profile = profileFunction.getProfile(ebr.timestamp) @@ -384,9 +395,10 @@ open class AutotuneIob @Inject constructor( ebr.toJson(true, it, dateUtil) } } + TherapyEvent.Type.CORRECTION_BOLUS -> bolusTreatment?.toJson(true, dateUtil) TherapyEvent.Type.CARBS_CORRECTION -> carbsTreatment?.toJson(true, dateUtil) - else -> cPjson + else -> cpJson } } } diff --git a/plugins/src/main/java/info/nightscout/plugins/general/autotune/AutotunePlugin.kt b/plugins/src/main/java/info/nightscout/plugins/general/autotune/AutotunePlugin.kt index d5c4e96f7e..489091dd06 100644 --- a/plugins/src/main/java/info/nightscout/plugins/general/autotune/AutotunePlugin.kt +++ b/plugins/src/main/java/info/nightscout/plugins/general/autotune/AutotunePlugin.kt @@ -6,7 +6,6 @@ import info.nightscout.androidaps.data.LocalInsulin import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.database.entities.UserEntry import info.nightscout.androidaps.database.entities.ValueWithUnit -import info.nightscout.androidaps.extensions.pureProfileFromJson import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.interfaces.Autotune import info.nightscout.interfaces.BuildHelper @@ -23,6 +22,7 @@ import info.nightscout.androidaps.utils.DateUtil import info.nightscout.interfaces.utils.JsonHelper import info.nightscout.interfaces.utils.MidnightTime import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson import info.nightscout.plugins.R import info.nightscout.plugins.general.autotune.data.ATProfile import info.nightscout.plugins.general.autotune.data.PreppedGlucose diff --git a/plugins/src/main/java/info/nightscout/plugins/general/autotune/data/ATProfile.kt b/plugins/src/main/java/info/nightscout/plugins/general/autotune/data/ATProfile.kt index 6cd077fe99..e0c101320a 100644 --- a/plugins/src/main/java/info/nightscout/plugins/general/autotune/data/ATProfile.kt +++ b/plugins/src/main/java/info/nightscout/plugins/general/autotune/data/ATProfile.kt @@ -7,9 +7,7 @@ import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.data.PureProfile import info.nightscout.androidaps.database.data.Block import info.nightscout.androidaps.extensions.blockValueBySeconds -import info.nightscout.androidaps.extensions.pureProfileFromJson import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.Insulin import info.nightscout.androidaps.interfaces.Profile @@ -17,8 +15,10 @@ import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileStore import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.interfaces.utils.Round import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.utils.Round import info.nightscout.rx.bus.RxBus import info.nightscout.shared.SafeParse import info.nightscout.shared.sharedPreferences.SP diff --git a/plugins/src/main/java/info/nightscout/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/plugins/src/main/java/info/nightscout/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index e67e5f1d6d..0af83f741a 100644 --- a/plugins/src/main/java/info/nightscout/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/plugins/src/main/java/info/nightscout/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -11,10 +11,8 @@ import androidx.work.Worker import androidx.work.WorkerParameters import androidx.work.workDataOf import dagger.android.HasAndroidInjector -import info.nightscout.interfaces.Constants import info.nightscout.androidaps.annotations.OpenForTesting import info.nightscout.androidaps.data.DetailedBolusInfo -import info.nightscout.interfaces.data.smsCommunicator.Sms import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.OfflineEvent import info.nightscout.androidaps.database.entities.TemporaryTarget @@ -29,32 +27,34 @@ import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.extensions.valueToUnitsString import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.CommandQueue -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraints import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.Loop import info.nightscout.androidaps.interfaces.PluginBase -import info.nightscout.interfaces.PluginDescription -import info.nightscout.interfaces.PluginType import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.PumpSync import info.nightscout.androidaps.interfaces.ResourceHelper -import info.nightscout.interfaces.SmsCommunicator import info.nightscout.androidaps.interfaces.XDripBroadcast import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification -import info.nightscout.interfaces.notifications.Notification import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider -import info.nightscout.interfaces.queue.Callback import info.nightscout.androidaps.receivers.DataWorkerStorage import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.textValidator.ValidatingEditTextPreference +import info.nightscout.interfaces.Config +import info.nightscout.interfaces.Constants +import info.nightscout.interfaces.PluginDescription +import info.nightscout.interfaces.PluginType +import info.nightscout.interfaces.SmsCommunicator +import info.nightscout.interfaces.data.smsCommunicator.Sms +import info.nightscout.interfaces.notifications.Notification +import info.nightscout.interfaces.queue.Callback import info.nightscout.plugins.R import info.nightscout.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui import info.nightscout.plugins.general.smsCommunicator.otp.OneTimePassword @@ -246,7 +246,7 @@ class SmsCommunicatorPlugin @Inject constructor( } fun processSms(receivedSms: Sms) { - if (!isEnabled(PluginType.GENERAL)) { + if (!isEnabled()) { aapsLogger.debug(LTag.SMS, "Ignoring SMS. Plugin disabled.") return } diff --git a/plugins/src/main/java/info/nightscout/plugins/general/xdripStatusline/StatusLinePlugin.kt b/plugins/src/main/java/info/nightscout/plugins/general/xdripStatusline/StatusLinePlugin.kt index c3877334ba..a443d545ab 100644 --- a/plugins/src/main/java/info/nightscout/plugins/general/xdripStatusline/StatusLinePlugin.kt +++ b/plugins/src/main/java/info/nightscout/plugins/general/xdripStatusline/StatusLinePlugin.kt @@ -111,7 +111,7 @@ class StatusLinePlugin @Inject constructor( private fun sendStatus() { var status = "" // sent once on disable val profile = profileFunction.getProfile() - if (isEnabled(PluginType.GENERAL) && profile != null) { + if (isEnabled() && profile != null) { status = buildStatusString(profile) } //sendData diff --git a/plugins/src/main/java/info/nightscout/plugins/profile/ProfilePlugin.kt b/plugins/src/main/java/info/nightscout/plugins/profile/ProfilePlugin.kt index 175df21037..1821f8ddc2 100644 --- a/plugins/src/main/java/info/nightscout/plugins/profile/ProfilePlugin.kt +++ b/plugins/src/main/java/info/nightscout/plugins/profile/ProfilePlugin.kt @@ -11,7 +11,6 @@ import info.nightscout.androidaps.annotations.OpenForTesting import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.data.PureProfile import info.nightscout.androidaps.extensions.blockFromJsonArray -import info.nightscout.androidaps.extensions.pureProfileFromJson import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.GlucoseUnit @@ -32,6 +31,7 @@ import info.nightscout.androidaps.utils.HardLimits import info.nightscout.interfaces.utils.JsonHelper import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson import info.nightscout.plugins.R import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventLocalProfileChanged diff --git a/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/BolusExtension.kt b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/BolusExtension.kt new file mode 100644 index 0000000000..166d58dba8 --- /dev/null +++ b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/BolusExtension.kt @@ -0,0 +1,52 @@ +package info.nightscout.plugins.sync.nsclient.extensions + +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.entities.Bolus +import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.interfaces.utils.JsonHelper +import org.json.JSONObject + +fun Bolus.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject = + JSONObject() + .put("eventType", if (type == Bolus.Type.SMB) TherapyEvent.Type.CORRECTION_BOLUS.text else TherapyEvent.Type.MEAL_BOLUS.text) + .put("insulin", amount) + .put("created_at", dateUtil.toISOString(timestamp)) + .put("date", timestamp) + .put("type", type.name) + .put("notes", notes) + .put("isValid", isValid) + .put("isSMB", type == Bolus.Type.SMB).also { + if (interfaceIDs.pumpId != null) it.put("pumpId", interfaceIDs.pumpId) + if (interfaceIDs.pumpType != null) it.put("pumpType", interfaceIDs.pumpType!!.name) + if (interfaceIDs.pumpSerial != null) it.put("pumpSerial", interfaceIDs.pumpSerial) + if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) + } + +fun bolusFromJson(jsonObject: JSONObject): Bolus? { + val timestamp = JsonHelper.safeGetLongAllowNull(jsonObject, "mills", null) ?: return null + val amount = JsonHelper.safeGetDoubleAllowNull(jsonObject, "insulin") ?: return null + val type = Bolus.Type.fromString(JsonHelper.safeGetString(jsonObject, "type")) + val isValid = JsonHelper.safeGetBoolean(jsonObject, "isValid", true) + val notes = JsonHelper.safeGetStringAllowNull(jsonObject, "notes", null) + val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null) ?: return null + val pumpId = JsonHelper.safeGetLongAllowNull(jsonObject, "pumpId", null) + val pumpType = InterfaceIDs.PumpType.fromString(JsonHelper.safeGetStringAllowNull(jsonObject, "pumpType", null)) + val pumpSerial = JsonHelper.safeGetStringAllowNull(jsonObject, "pumpSerial", null) + + if (timestamp == 0L) return null + if (amount == 0.0) return null + + return Bolus( + timestamp = timestamp, + amount = amount, + type = type, + notes = notes, + isValid = isValid, + ).also { + it.interfaceIDs.nightscoutId = id + it.interfaceIDs.pumpId = pumpId + it.interfaceIDs.pumpType = pumpType + it.interfaceIDs.pumpSerial = pumpSerial + } +} \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/CarbsExtension.kt b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/CarbsExtension.kt similarity index 86% rename from core/src/main/java/info/nightscout/androidaps/extensions/CarbsExtension.kt rename to plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/CarbsExtension.kt index fecf881479..3a09d8165f 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/CarbsExtension.kt +++ b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/CarbsExtension.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.extensions +package info.nightscout.plugins.sync.nsclient.extensions import info.nightscout.androidaps.database.embedments.InterfaceIDs import info.nightscout.androidaps.database.entities.Carbs @@ -22,19 +22,6 @@ fun Carbs.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject = if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) } -/* - create fake object with nsID and isValid == false - */ -fun carbsFromNsIdForInvalidating(nsId: String): Carbs = - carbsFromJson( - JSONObject() - .put("mills", 1) - .put("carbs", -1.0) - .put("notes", null) - .put("_id", nsId) - .put("isValid", false) - )!! - fun carbsFromJson(jsonObject: JSONObject): Carbs? { val timestamp = JsonHelper.safeGetLongAllowNull(jsonObject, "mills", null) ?: return null val duration = JsonHelper.safeGetLong(jsonObject, "duration") diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/EffectiveProfileSwitchExtension.kt b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/EffectiveProfileSwitchExtension.kt similarity index 97% rename from core/src/main/java/info/nightscout/androidaps/extensions/EffectiveProfileSwitchExtension.kt rename to plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/EffectiveProfileSwitchExtension.kt index 517d12f81f..18e32b5dd3 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/EffectiveProfileSwitchExtension.kt +++ b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/EffectiveProfileSwitchExtension.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.extensions +package info.nightscout.plugins.sync.nsclient.extensions import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.database.embedments.InterfaceIDs @@ -6,6 +6,7 @@ import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson import info.nightscout.interfaces.utils.JsonHelper import org.json.JSONObject diff --git a/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/ExtendedBolusExtension.kt b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/ExtendedBolusExtension.kt new file mode 100644 index 0000000000..a6bdf85da5 --- /dev/null +++ b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/ExtendedBolusExtension.kt @@ -0,0 +1,74 @@ +package info.nightscout.plugins.sync.nsclient.extensions + +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.entities.ExtendedBolus +import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.extensions.toTemporaryBasal +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.plugins.sync.nsclient.extensions.toJson +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.T +import info.nightscout.interfaces.utils.JsonHelper +import org.json.JSONObject + +fun ExtendedBolus.toJson(isAdd: Boolean, profile: Profile, dateUtil: DateUtil): JSONObject = + if (isEmulatingTempBasal) + toTemporaryBasal(profile) + .toJson(isAdd, profile, dateUtil) + .put("extendedEmulated", toRealJson(isAdd, dateUtil)) + else toRealJson(isAdd, dateUtil) + +fun ExtendedBolus.toRealJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject = + JSONObject() + .put("created_at", dateUtil.toISOString(timestamp)) + .put("enteredBy", "openaps://" + "AndroidAPS") + .put("eventType", TherapyEvent.Type.COMBO_BOLUS.text) + .put("duration", T.msecs(duration).mins()) + .put("durationInMilliseconds", duration) + .put("splitNow", 0) + .put("splitExt", 100) + .put("enteredinsulin", amount) + .put("relative", rate) + .put("isValid", isValid) + .put("isEmulatingTempBasal", isEmulatingTempBasal) + .also { + if (interfaceIDs.pumpId != null) it.put("pumpId", interfaceIDs.pumpId) + if (interfaceIDs.endId != null) it.put("endId", interfaceIDs.endId) + if (interfaceIDs.pumpType != null) it.put("pumpType", interfaceIDs.pumpType!!.name) + if (interfaceIDs.pumpSerial != null) it.put("pumpSerial", interfaceIDs.pumpSerial) + if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) + } + +fun extendedBolusFromJson(jsonObject: JSONObject): ExtendedBolus? { + val timestamp = JsonHelper.safeGetLongAllowNull(jsonObject, "mills", null) ?: return null + if (JsonHelper.safeGetIntAllowNull(jsonObject, "splitNow") != 0) return null + if (JsonHelper.safeGetIntAllowNull(jsonObject, "splitExt") != 100) return null + val amount = JsonHelper.safeGetDoubleAllowNull(jsonObject, "enteredinsulin") ?: return null + val duration = JsonHelper.safeGetLongAllowNull(jsonObject, "duration") ?: return null + val durationInMilliseconds = JsonHelper.safeGetLongAllowNull(jsonObject, "durationInMilliseconds") + val isValid = JsonHelper.safeGetBoolean(jsonObject, "isValid", true) + val isEmulatingTempBasal = JsonHelper.safeGetBoolean(jsonObject, "isEmulatingTempBasal", false) + val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null) ?: return null + val pumpId = JsonHelper.safeGetLongAllowNull(jsonObject, "pumpId", null) + val endPumpId = JsonHelper.safeGetLongAllowNull(jsonObject, "endId", null) + val pumpType = InterfaceIDs.PumpType.fromString(JsonHelper.safeGetStringAllowNull(jsonObject, "pumpType", null)) + val pumpSerial = JsonHelper.safeGetStringAllowNull(jsonObject, "pumpSerial", null) + + if (timestamp == 0L) return null + if (duration == 0L && durationInMilliseconds == 0L) return null + if (amount == 0.0) return null + + return ExtendedBolus( + timestamp = timestamp, + amount = amount, + duration = durationInMilliseconds ?: T.mins(duration).msecs(), + isEmulatingTempBasal = isEmulatingTempBasal, + isValid = isValid + ).also { + it.interfaceIDs.nightscoutId = id + it.interfaceIDs.pumpId = pumpId + it.interfaceIDs.endId = endPumpId + it.interfaceIDs.pumpType = pumpType + it.interfaceIDs.pumpSerial = pumpSerial + } +} \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/OfflineEventExtension.kt b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/OfflineEventExtension.kt similarity index 97% rename from core/src/main/java/info/nightscout/androidaps/extensions/OfflineEventExtension.kt rename to plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/OfflineEventExtension.kt index c150fd643c..cc729cb1d4 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/OfflineEventExtension.kt +++ b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/OfflineEventExtension.kt @@ -1,11 +1,11 @@ -package info.nightscout.androidaps.extensions +package info.nightscout.plugins.sync.nsclient.extensions import info.nightscout.androidaps.database.embedments.InterfaceIDs import info.nightscout.androidaps.database.entities.OfflineEvent import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.interfaces.utils.JsonHelper import info.nightscout.androidaps.utils.T +import info.nightscout.interfaces.utils.JsonHelper import org.json.JSONObject fun OfflineEvent.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject = diff --git a/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/ProfileSwitchExtension.kt b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/ProfileSwitchExtension.kt new file mode 100644 index 0000000000..58dd2466e7 --- /dev/null +++ b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/ProfileSwitchExtension.kt @@ -0,0 +1,96 @@ +package info.nightscout.plugins.sync.nsclient.extensions + +import info.nightscout.androidaps.data.ProfileSealed +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.entities.ProfileSwitch +import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.extensions.fromConstant +import info.nightscout.androidaps.utils.extensions.getCustomizedName +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson +import info.nightscout.interfaces.utils.JsonHelper +import org.json.JSONObject + +fun ProfileSwitch.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject = + JSONObject() + .put("timeshift", timeshift) + .put("percentage", percentage) + .put("duration", T.msecs(duration).mins()) + .put("profile", getCustomizedName()) + .put("originalProfileName", profileName) + .put("originalDuration", duration) + .put("created_at", dateUtil.toISOString(timestamp)) + .put("enteredBy", "openaps://" + "AndroidAPS") + .put("isValid", isValid) + .put("eventType", TherapyEvent.Type.PROFILE_SWITCH.text) + .also { // remove customization to store original profileJson in toPureNsJson call + timeshift = 0 + percentage = 100 + } + .put("profileJson", ProfileSealed.PS(this).toPureNsJson(dateUtil).toString()) + .also { + if (interfaceIDs.pumpId != null) it.put("pumpId", interfaceIDs.pumpId) + if (interfaceIDs.pumpType != null) it.put("pumpType", interfaceIDs.pumpType!!.name) + if (interfaceIDs.pumpSerial != null) it.put("pumpSerial", interfaceIDs.pumpSerial) + if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) + } + +/* NS PS +{ + "_id":"608ffa268db0676196a772d7", + "enteredBy":"undefined", + "eventType":"Profile Switch", + "duration":10, + "profile":"LocalProfile0", + "created_at":"2021-05-03T13:26:58.537Z", + "utcOffset":0, + "mills":1620048418537, + "mgdl":98 +} + */ +fun profileSwitchFromJson(jsonObject: JSONObject, dateUtil: DateUtil, activePlugin: ActivePlugin): ProfileSwitch? { + val timestamp = JsonHelper.safeGetLongAllowNull(jsonObject, "mills", null) ?: return null + val duration = JsonHelper.safeGetLong(jsonObject, "duration") + val originalDuration = JsonHelper.safeGetLongAllowNull(jsonObject, "originalDuration") + val timeshift = JsonHelper.safeGetLong(jsonObject, "timeshift") + val percentage = JsonHelper.safeGetInt(jsonObject, "percentage", 100) + val isValid = JsonHelper.safeGetBoolean(jsonObject, "isValid", true) + val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null) + val profileName = JsonHelper.safeGetStringAllowNull(jsonObject, "profile", null) ?: return null + val originalProfileName = JsonHelper.safeGetStringAllowNull(jsonObject, "originalProfileName", null) + val profileJson = JsonHelper.safeGetStringAllowNull(jsonObject, "profileJson", null) + val pumpId = JsonHelper.safeGetLongAllowNull(jsonObject, "pumpId", null) + val pumpType = InterfaceIDs.PumpType.fromString(JsonHelper.safeGetStringAllowNull(jsonObject, "pumpType", null)) + val pumpSerial = JsonHelper.safeGetStringAllowNull(jsonObject, "pumpSerial", null) + + if (timestamp == 0L) return null + val pureProfile = + if (profileJson == null) { // entered through NS, no JSON attached + val profilePlugin = activePlugin.activeProfileSource + val store = profilePlugin.profile ?: return null + store.getSpecificProfile(profileName) ?: return null + } else pureProfileFromJson(JSONObject(profileJson), dateUtil) ?: return null + val profileSealed = ProfileSealed.Pure(pureProfile) + + return ProfileSwitch( + timestamp = timestamp, + basalBlocks = profileSealed.basalBlocks, + isfBlocks = profileSealed.isfBlocks, + icBlocks = profileSealed.icBlocks, + targetBlocks = profileSealed.targetBlocks, + glucoseUnit = ProfileSwitch.GlucoseUnit.fromConstant(profileSealed.units), + profileName = originalProfileName ?: profileName, + timeshift = timeshift, + percentage = percentage, + duration = originalDuration ?: T.mins(duration).msecs(), + insulinConfiguration = profileSealed.insulinConfiguration, + isValid = isValid + ).also { + it.interfaceIDs.nightscoutId = id + it.interfaceIDs.pumpId = pumpId + it.interfaceIDs.pumpType = pumpType + it.interfaceIDs.pumpSerial = pumpSerial + } +} diff --git a/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/TemporaryBasalExtension.kt b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/TemporaryBasalExtension.kt new file mode 100644 index 0000000000..92afb9d5c8 --- /dev/null +++ b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/TemporaryBasalExtension.kt @@ -0,0 +1,74 @@ +package info.nightscout.plugins.sync.nsclient.extensions + +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.entities.TemporaryBasal +import info.nightscout.androidaps.database.entities.TemporaryBasal.Type.Companion.fromString +import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.extensions.convertedToAbsolute +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.T +import info.nightscout.interfaces.utils.JsonHelper +import org.json.JSONObject + +fun TemporaryBasal.toJson(isAdd: Boolean, profile: Profile, dateUtil: DateUtil): JSONObject = + JSONObject() + .put("created_at", dateUtil.toISOString(timestamp)) + .put("enteredBy", "openaps://" + "AndroidAPS") + .put("eventType", TherapyEvent.Type.TEMPORARY_BASAL.text) + .put("isValid", isValid) + .put("duration", T.msecs(duration).mins()) + .put("durationInMilliseconds", duration) // rounded duration leads to different basal IOB + .put("type", type.name) + .put("rate", convertedToAbsolute(timestamp, profile)) // generated by OpenAPS, for compatibility + .also { + if (isAbsolute) it.put("absolute", rate) + else it.put("percent", rate - 100) + if (interfaceIDs.pumpId != null) it.put("pumpId", interfaceIDs.pumpId) + if (interfaceIDs.endId != null) it.put("endId", interfaceIDs.endId) + if (interfaceIDs.pumpType != null) it.put("pumpType", interfaceIDs.pumpType!!.name) + if (interfaceIDs.pumpSerial != null) it.put("pumpSerial", interfaceIDs.pumpSerial) + if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) + } + +fun temporaryBasalFromJson(jsonObject: JSONObject): TemporaryBasal? { + val timestamp = JsonHelper.safeGetLongAllowNull(jsonObject, "mills", null) ?: return null + val percent = JsonHelper.safeGetDoubleAllowNull(jsonObject, "percent") + val absolute = JsonHelper.safeGetDoubleAllowNull(jsonObject, "absolute") + val duration = JsonHelper.safeGetLongAllowNull(jsonObject, "duration") ?: return null + val durationInMilliseconds = JsonHelper.safeGetLongAllowNull(jsonObject, "durationInMilliseconds") + val type = fromString(JsonHelper.safeGetString(jsonObject, "type")) + val isValid = JsonHelper.safeGetBoolean(jsonObject, "isValid", true) + val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null) ?: return null + val pumpId = JsonHelper.safeGetLongAllowNull(jsonObject, "pumpId", null) + val endPumpId = JsonHelper.safeGetLongAllowNull(jsonObject, "endId", null) + val pumpType = InterfaceIDs.PumpType.fromString(JsonHelper.safeGetStringAllowNull(jsonObject, "pumpType", null)) + val pumpSerial = JsonHelper.safeGetStringAllowNull(jsonObject, "pumpSerial", null) + + val rate: Double + val isAbsolute: Boolean + if (absolute != null) { + rate = absolute + isAbsolute = true + } else if (percent != null) { + rate = percent + 100 + isAbsolute = false + } else return null + if (duration == 0L && durationInMilliseconds == null) return null + if (timestamp == 0L) return null + + return TemporaryBasal( + timestamp = timestamp, + rate = rate, + duration = durationInMilliseconds ?: T.mins(duration).msecs(), + type = type, + isAbsolute = isAbsolute, + isValid = isValid + ).also { + it.interfaceIDs.nightscoutId = id + it.interfaceIDs.pumpId = pumpId + it.interfaceIDs.endId = endPumpId + it.interfaceIDs.pumpType = pumpType + it.interfaceIDs.pumpSerial = pumpSerial + } +} \ No newline at end of file diff --git a/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/TemporaryTargetExtension.kt b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/TemporaryTargetExtension.kt new file mode 100644 index 0000000000..f924c7c45c --- /dev/null +++ b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/TemporaryTargetExtension.kt @@ -0,0 +1,66 @@ +package info.nightscout.plugins.sync.nsclient.extensions + +import info.nightscout.androidaps.database.entities.TemporaryTarget +import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.interfaces.GlucoseUnit +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.T +import info.nightscout.interfaces.Constants +import info.nightscout.interfaces.utils.JsonHelper +import org.json.JSONObject + +fun temporaryTargetFromJson(jsonObject: JSONObject): TemporaryTarget? { + val units = GlucoseUnit.fromText(JsonHelper.safeGetString(jsonObject, "units", Constants.MGDL)) + val timestamp = JsonHelper.safeGetLongAllowNull(jsonObject, "mills", null) ?: return null + val duration = JsonHelper.safeGetLongAllowNull(jsonObject, "duration", null) ?: return null + val durationInMilliseconds = JsonHelper.safeGetLongAllowNull(jsonObject, "durationInMilliseconds") + var low = JsonHelper.safeGetDouble(jsonObject, "targetBottom") + low = Profile.toMgdl(low, units) + var high = JsonHelper.safeGetDouble(jsonObject, "targetTop") + high = Profile.toMgdl(high, units) + val reasonString = if (duration != 0L) JsonHelper.safeGetStringAllowNull(jsonObject, "reason", null) + ?: return null else "" + // this string can be localized from NS, it will not work in this case CUSTOM will be used + val reason = TemporaryTarget.Reason.fromString(reasonString) + val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null) ?: return null + val isValid = JsonHelper.safeGetBoolean(jsonObject, "isValid", true) + + if (timestamp == 0L) return null + + if (duration > 0L) { + // not ending event + if (low < Constants.MIN_TT_MGDL) return null + if (low > Constants.MAX_TT_MGDL) return null + if (high < Constants.MIN_TT_MGDL) return null + if (high > Constants.MAX_TT_MGDL) return null + if (low > high) return null + } + val tt = TemporaryTarget( + timestamp = timestamp, + duration = durationInMilliseconds ?: T.mins(duration).msecs(), + reason = reason, + lowTarget = low, + highTarget = high, + isValid = isValid + ) + tt.interfaceIDs.nightscoutId = id + return tt +} + +fun TemporaryTarget.toJson(isAdd: Boolean, units: GlucoseUnit, dateUtil: DateUtil): JSONObject = + JSONObject() + .put("eventType", TherapyEvent.Type.TEMPORARY_TARGET.text) + .put("duration", T.msecs(duration).mins()) + .put("durationInMilliseconds", duration) + .put("isValid", isValid) + .put("created_at", dateUtil.toISOString(timestamp)) + .put("timestamp", timestamp) + .put("enteredBy", "AndroidAPS").also { + if (lowTarget > 0) it + .put("reason", reason.text) + .put("targetBottom", Profile.fromMgdlToUnits(lowTarget, units)) + .put("targetTop", Profile.fromMgdlToUnits(highTarget, units)) + .put("units", units.asText) + if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) + } diff --git a/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/TherapyEventExtension.kt b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/TherapyEventExtension.kt new file mode 100644 index 0000000000..1210bdd96c --- /dev/null +++ b/plugins/src/main/java/info/nightscout/plugins/sync/nsclient/extensions/TherapyEventExtension.kt @@ -0,0 +1,87 @@ +package info.nightscout.plugins.sync.nsclient.extensions + +import info.nightscout.androidaps.core.R +import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.interfaces.GlucoseUnit +import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.T +import info.nightscout.interfaces.Constants +import info.nightscout.interfaces.utils.JsonHelper +import org.json.JSONObject +import java.util.concurrent.TimeUnit + +fun TherapyEvent.age(useShortText: Boolean, rh: ResourceHelper, dateUtil: DateUtil): String { + val diff = dateUtil.computeDiff(timestamp, System.currentTimeMillis()) + var days = " " + rh.gs(R.string.days) + " " + var hours = " " + rh.gs(R.string.hours) + " " + if (useShortText) { + days = rh.gs(R.string.shortday) + hours = rh.gs(R.string.shorthour) + } + return diff[TimeUnit.DAYS].toString() + days + diff[TimeUnit.HOURS] + hours +} + +fun TherapyEvent.GlucoseUnit.toMainUnit(): GlucoseUnit = + if (this == TherapyEvent.GlucoseUnit.MGDL) GlucoseUnit.MGDL + else GlucoseUnit.MMOL + +/* + create fake object with nsID and isValid == false + */ + +fun therapyEventFromJson(jsonObject: JSONObject): TherapyEvent? { + val glucoseUnit = if (JsonHelper.safeGetString(jsonObject, "units", Constants.MGDL) == Constants.MGDL) TherapyEvent.GlucoseUnit.MGDL else TherapyEvent.GlucoseUnit.MMOL + val timestamp = JsonHelper.safeGetLongAllowNull(jsonObject, "mills", null) ?: return null + val type = TherapyEvent.Type.fromString(JsonHelper.safeGetString(jsonObject, "eventType", TherapyEvent.Type.NONE.text)) + val duration = JsonHelper.safeGetLong(jsonObject, "duration") + val durationInMilliseconds = JsonHelper.safeGetLongAllowNull(jsonObject, "durationInMilliseconds") + val glucose = JsonHelper.safeGetDoubleAllowNull(jsonObject, "glucose") + val glucoseType = TherapyEvent.MeterType.fromString(JsonHelper.safeGetString(jsonObject, "glucoseType")) + val enteredBy = JsonHelper.safeGetStringAllowNull(jsonObject, "enteredBy", null) + val note = JsonHelper.safeGetStringAllowNull(jsonObject, "notes", null) + val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null) ?: return null + val isValid = JsonHelper.safeGetBoolean(jsonObject, "isValid", true) + + if (timestamp == 0L) return null + + val te = TherapyEvent( + timestamp = timestamp, + duration = durationInMilliseconds ?: T.mins(duration).msecs(), + glucoseUnit = glucoseUnit, + type = type, + glucose = glucose, + glucoseType = glucoseType, + enteredBy = enteredBy, + note = note, + isValid = isValid + ) + te.interfaceIDs.nightscoutId = id + return te +} + +fun TherapyEvent.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject = + JSONObject() + .put("eventType", type.text) + .put("isValid", isValid) + .put("created_at", dateUtil.toISOString(timestamp)) + .put("enteredBy", enteredBy) + .put("units", if (glucoseUnit == TherapyEvent.GlucoseUnit.MGDL) Constants.MGDL else Constants.MMOL) + .also { + if (duration != 0L) it.put("duration", T.msecs(duration).mins()) + if (duration != 0L) it.put("durationInMilliseconds", duration) + if (note != null) it.put("notes", note) + if (glucose != null) it.put("glucose", glucose) + if (glucoseType != null) it.put("glucoseType", glucoseType!!.text) + if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) + if (type == TherapyEvent.Type.ANNOUNCEMENT) it.put("isAnnouncement", true) + } + +fun List.isTherapyEventEvent5minBack(time: Long): Boolean { + for (event in this) { + if (event.timestamp <= time && event.timestamp > time - T.mins(5).msecs()) { + return true + } + } + return false +} diff --git a/plugins/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt b/plugins/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt index 78bd435dc5..c8c0e6e0e3 100644 --- a/plugins/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt +++ b/plugins/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt @@ -6,15 +6,15 @@ import dagger.android.HasAndroidInjector import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.database.embedments.InsulinConfiguration import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch -import info.nightscout.androidaps.extensions.pureProfileFromJson import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileStore import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson +import info.nightscout.interfaces.Config import info.nightscout.rx.bus.RxBus import org.json.JSONObject import org.junit.Before diff --git a/pump/combo/build.gradle b/pump/combo/build.gradle index d1fd47f9bc..b89823c385 100644 --- a/pump/combo/build.gradle +++ b/pump/combo/build.gradle @@ -1,11 +1,14 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" android { diff --git a/pump/dana/build.gradle b/pump/dana/build.gradle index b0e76a4a93..2b95c5c310 100644 --- a/pump/dana/build.gradle +++ b/pump/dana/build.gradle @@ -1,11 +1,14 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" diff --git a/pump/dana/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt b/pump/dana/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt index d6ee4f06e1..dfcd01ae5c 100644 --- a/pump/dana/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt +++ b/pump/dana/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt @@ -3,7 +3,7 @@ package info.nightscout.androidaps import dagger.android.AndroidInjector import dagger.android.HasAndroidInjector import info.nightscout.androidaps.data.ProfileSealed -import info.nightscout.androidaps.extensions.pureProfileFromJson +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.Profile diff --git a/pump/danar/build.gradle b/pump/danar/build.gradle index ff7ca88e4e..a42d20830f 100644 --- a/pump/danar/build.gradle +++ b/pump/danar/build.gradle @@ -1,11 +1,14 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" android { diff --git a/pump/danar/src/main/java/info/nightscout/androidaps/danaRKorean/DanaRKoreanPlugin.kt b/pump/danar/src/main/java/info/nightscout/androidaps/danaRKorean/DanaRKoreanPlugin.kt index 01aa0e3bd4..982a276f1c 100644 --- a/pump/danar/src/main/java/info/nightscout/androidaps/danaRKorean/DanaRKoreanPlugin.kt +++ b/pump/danar/src/main/java/info/nightscout/androidaps/danaRKorean/DanaRKoreanPlugin.kt @@ -11,7 +11,6 @@ import info.nightscout.androidaps.danaRKorean.services.DanaRKoreanExecutionServi import info.nightscout.androidaps.danar.AbstractDanaRPlugin import info.nightscout.androidaps.danar.R import info.nightscout.androidaps.data.DetailedBolusInfo -import info.nightscout.interfaces.data.PumpEnactResult import info.nightscout.androidaps.data.PumpEnactResultImpl import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.interfaces.ActivePlugin @@ -22,15 +21,15 @@ import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.PumpSync import info.nightscout.androidaps.interfaces.PumpSync.TemporaryBasalType import info.nightscout.androidaps.interfaces.ResourceHelper -import info.nightscout.rx.events.EventOverviewBolusProgress import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.interfaces.data.PumpEnactResult import info.nightscout.interfaces.utils.Round -import info.nightscout.interfaces.PluginType import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventAppExit +import info.nightscout.rx.events.EventOverviewBolusProgress import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag import info.nightscout.shared.sharedPreferences.SP @@ -70,14 +69,14 @@ class DanaRKoreanPlugin @Inject constructor( .toObservable(EventPreferenceChange::class.java) .observeOn(aapsSchedulers.io) .subscribe({ - if (isEnabled(PluginType.PUMP)) { - val previousValue = useExtendedBoluses - useExtendedBoluses = sp.getBoolean(R.string.key_danar_useextended, false) - if (useExtendedBoluses != previousValue && pumpSync.expectedPumpState().extendedBolus != null) { - sExecutionService.extendedBolusStop() - } - } - }, fabricPrivacy::logException) + if (isEnabled()) { + val previousValue = useExtendedBoluses + useExtendedBoluses = sp.getBoolean(R.string.key_danar_useextended, false) + if (useExtendedBoluses != previousValue && pumpSync.expectedPumpState().extendedBolus != null) { + sExecutionService.extendedBolusStop() + } + } + }, fabricPrivacy::logException) disposable += rxBus .toObservable(EventAppExit::class.java) .observeOn(aapsSchedulers.io) diff --git a/pump/danar/src/main/java/info/nightscout/androidaps/danar/DanaRPlugin.java b/pump/danar/src/main/java/info/nightscout/androidaps/danar/DanaRPlugin.java index 7348f45bca..bc44f3af7c 100644 --- a/pump/danar/src/main/java/info/nightscout/androidaps/danar/DanaRPlugin.java +++ b/pump/danar/src/main/java/info/nightscout/androidaps/danar/DanaRPlugin.java @@ -85,7 +85,7 @@ public class DanaRPlugin extends AbstractDanaRPlugin { .toObservable(EventPreferenceChange.class) .observeOn(aapsSchedulers.getIo()) .subscribe(event -> { - if (isEnabled(PluginType.PUMP)) { + if (isEnabled()) { boolean previousValue = useExtendedBoluses; useExtendedBoluses = sp.getBoolean(R.string.key_danar_useextended, false); diff --git a/pump/danar/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt b/pump/danar/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt index c6422c2fa3..c2ae58502b 100644 --- a/pump/danar/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt +++ b/pump/danar/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt @@ -4,7 +4,7 @@ import android.content.Context import dagger.android.AndroidInjector import dagger.android.HasAndroidInjector import info.nightscout.androidaps.data.ProfileSealed -import info.nightscout.androidaps.extensions.pureProfileFromJson +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.IobCobCalculator diff --git a/pump/danars/build.gradle b/pump/danars/build.gradle index 4b0c9c7502..746fc08c41 100644 --- a/pump/danars/build.gradle +++ b/pump/danars/build.gradle @@ -1,11 +1,14 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" diff --git a/pump/danars/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt b/pump/danars/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt index ba7d59b17f..b1a3540546 100644 --- a/pump/danars/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt +++ b/pump/danars/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt @@ -3,7 +3,7 @@ package info.nightscout.androidaps import dagger.android.AndroidInjector import dagger.android.HasAndroidInjector import info.nightscout.androidaps.data.ProfileSealed -import info.nightscout.androidaps.extensions.pureProfileFromJson +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.Profile diff --git a/pump/diaconn/build.gradle b/pump/diaconn/build.gradle index c036072701..4aa1f76d4b 100644 --- a/pump/diaconn/build.gradle +++ b/pump/diaconn/build.gradle @@ -1,11 +1,14 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" diff --git a/pump/eopatch/build.gradle b/pump/eopatch/build.gradle index ae89e54fdd..b117f5d123 100644 --- a/pump/eopatch/build.gradle +++ b/pump/eopatch/build.gradle @@ -1,8 +1,10 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" diff --git a/pump/medtronic/build.gradle b/pump/medtronic/build.gradle index a2f71383c8..337bb98cbf 100644 --- a/pump/medtronic/build.gradle +++ b/pump/medtronic/build.gradle @@ -1,11 +1,14 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" android { diff --git a/pump/omnipod-common/build.gradle b/pump/omnipod-common/build.gradle index ba9c9049be..4c92c7eace 100644 --- a/pump/omnipod-common/build.gradle +++ b/pump/omnipod-common/build.gradle @@ -1,12 +1,14 @@ - -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" android { diff --git a/pump/omnipod-dash/build.gradle b/pump/omnipod-dash/build.gradle index ca810fd435..c9f3cc718a 100644 --- a/pump/omnipod-dash/build.gradle +++ b/pump/omnipod-dash/build.gradle @@ -1,13 +1,16 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' -apply plugin: "io.gitlab.arturbosch.detekt" // TODO move to `subprojects` section in global build.gradle -apply plugin: "org.jlleitschuh.gradle.ktlint" // TODO move to `subprojects` section in global build.gradle +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' + id "io.gitlab.arturbosch.detekt" // TODO move to `subprojects` section in global build.gradle + id "org.jlleitschuh.gradle.ktlint" // TODO move to `subprojects` section in global build.gradle +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" diff --git a/pump/omnipod-eros/build.gradle b/pump/omnipod-eros/build.gradle index 86bc40cf90..7b36bb3f18 100644 --- a/pump/omnipod-eros/build.gradle +++ b/pump/omnipod-eros/build.gradle @@ -1,11 +1,14 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" diff --git a/pump/pump-common/build.gradle b/pump/pump-common/build.gradle index 2ffd366f4a..2c974a7c88 100644 --- a/pump/pump-common/build.gradle +++ b/pump/pump-common/build.gradle @@ -1,11 +1,14 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" android { diff --git a/pump/rileylink/build.gradle b/pump/rileylink/build.gradle index d0c2e070ea..eb5621dc5a 100644 --- a/pump/rileylink/build.gradle +++ b/pump/rileylink/build.gradle @@ -1,11 +1,14 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/allopen_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" android { diff --git a/settings.gradle b/settings.gradle index b7634b6113..df262404ac 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,6 +7,7 @@ include ':app-wear-shared:shared' include ':app-wear-shared:shared-impl' include ':graphview' include ':libraries' +include ':ns-sdk' include ':ui' include ':implementation' include ':plugins' diff --git a/ui/build.gradle b/ui/build.gradle index 6abc4cbb62..f7ca3d31e6 100644 --- a/ui/build.gradle +++ b/ui/build.gradle @@ -1,13 +1,16 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/android_dependencies.gradle" apply from: "${project.rootDir}/core/android_module_dependencies.gradle" apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" + android { namespace 'info.nightscout.ui' } diff --git a/ui/src/main/java/info/nightscout/ui/activities/fragments/TreatmentsBolusCarbsFragment.kt b/ui/src/main/java/info/nightscout/ui/activities/fragments/TreatmentsBolusCarbsFragment.kt index 194de5288f..2108831e51 100644 --- a/ui/src/main/java/info/nightscout/ui/activities/fragments/TreatmentsBolusCarbsFragment.kt +++ b/ui/src/main/java/info/nightscout/ui/activities/fragments/TreatmentsBolusCarbsFragment.kt @@ -31,7 +31,6 @@ import info.nightscout.androidaps.events.EventNewHistoryData import info.nightscout.androidaps.extensions.iobCalc import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.BuildHelper import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.logging.UserEntryLogger @@ -41,6 +40,7 @@ import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog +import info.nightscout.interfaces.BuildHelper import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventNSClientRestart diff --git a/ui/src/main/java/info/nightscout/ui/activities/fragments/TreatmentsCareportalFragment.kt b/ui/src/main/java/info/nightscout/ui/activities/fragments/TreatmentsCareportalFragment.kt index cb770001a5..bad94659fd 100644 --- a/ui/src/main/java/info/nightscout/ui/activities/fragments/TreatmentsCareportalFragment.kt +++ b/ui/src/main/java/info/nightscout/ui/activities/fragments/TreatmentsCareportalFragment.kt @@ -2,14 +2,18 @@ package info.nightscout.ui.activities.fragments import android.os.Bundle import android.util.SparseArray -import android.view.* +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup import androidx.core.util.forEach import androidx.core.view.MenuProvider import androidx.lifecycle.Lifecycle import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment -import info.nightscout.ui.activities.fragments.TreatmentsCareportalFragment.RecyclerViewAdapter.TherapyEventsViewHolder import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.database.entities.UserEntry.Action @@ -17,21 +21,26 @@ import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.transactions.InvalidateAAPSStartedTherapyEventTransaction import info.nightscout.androidaps.database.transactions.InvalidateTherapyEventTransaction -import info.nightscout.rx.events.EventNSClientRestart -import info.nightscout.rx.events.EventTherapyEventChange import info.nightscout.androidaps.extensions.toVisibility -import info.nightscout.interfaces.BuildHelper import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.logging.UserEntryLogger -import info.nightscout.rx.bus.RxBus -import info.nightscout.androidaps.utils.* +import info.nightscout.androidaps.utils.ActionModeHelper +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.ToastUtils +import info.nightscout.androidaps.utils.Translator import info.nightscout.androidaps.utils.alertDialogs.OKDialog +import info.nightscout.interfaces.BuildHelper import info.nightscout.rx.AapsSchedulers +import info.nightscout.rx.bus.RxBus +import info.nightscout.rx.events.EventNSClientRestart +import info.nightscout.rx.events.EventTherapyEventChange import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag - import info.nightscout.shared.sharedPreferences.SP import info.nightscout.ui.R +import info.nightscout.ui.activities.fragments.TreatmentsCareportalFragment.RecyclerViewAdapter.TherapyEventsViewHolder import info.nightscout.ui.databinding.TreatmentsCareportalFragmentBinding import info.nightscout.ui.databinding.TreatmentsCareportalItemBinding import io.reactivex.rxjava3.core.Completable diff --git a/ui/src/main/java/info/nightscout/ui/activities/fragments/TreatmentsProfileSwitchFragment.kt b/ui/src/main/java/info/nightscout/ui/activities/fragments/TreatmentsProfileSwitchFragment.kt index 05bccb89f8..8e42550b37 100644 --- a/ui/src/main/java/info/nightscout/ui/activities/fragments/TreatmentsProfileSwitchFragment.kt +++ b/ui/src/main/java/info/nightscout/ui/activities/fragments/TreatmentsProfileSwitchFragment.kt @@ -25,26 +25,25 @@ import info.nightscout.androidaps.database.transactions.InvalidateProfileSwitchT import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged import info.nightscout.androidaps.events.EventNewHistoryData -import info.nightscout.rx.events.EventLocalProfileChanged -import info.nightscout.rx.events.EventNSClientRestart -import info.nightscout.rx.events.EventProfileSwitchChanged -import info.nightscout.androidaps.extensions.getCustomizedName import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.BuildHelper import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.logging.UserEntryLogger -import info.nightscout.rx.bus.RxBus import info.nightscout.androidaps.utils.ActionModeHelper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog +import info.nightscout.androidaps.utils.extensions.getCustomizedName +import info.nightscout.interfaces.BuildHelper import info.nightscout.rx.AapsSchedulers +import info.nightscout.rx.bus.RxBus +import info.nightscout.rx.events.EventLocalProfileChanged +import info.nightscout.rx.events.EventNSClientRestart +import info.nightscout.rx.events.EventProfileSwitchChanged import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag - import info.nightscout.shared.sharedPreferences.SP import info.nightscout.ui.R import info.nightscout.ui.activities.fragments.TreatmentsProfileSwitchFragment.RecyclerProfileViewAdapter.ProfileSwitchViewHolder diff --git a/ui/src/main/java/info/nightscout/ui/activities/fragments/TreatmentsTempTargetFragment.kt b/ui/src/main/java/info/nightscout/ui/activities/fragments/TreatmentsTempTargetFragment.kt index 3b01bc3e3a..c6a8686caf 100644 --- a/ui/src/main/java/info/nightscout/ui/activities/fragments/TreatmentsTempTargetFragment.kt +++ b/ui/src/main/java/info/nightscout/ui/activities/fragments/TreatmentsTempTargetFragment.kt @@ -29,7 +29,6 @@ import info.nightscout.androidaps.extensions.friendlyDescription import info.nightscout.androidaps.extensions.highValueToUnitsToString import info.nightscout.androidaps.extensions.lowValueToUnitsToString import info.nightscout.androidaps.extensions.toVisibility -import info.nightscout.interfaces.BuildHelper import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.logging.UserEntryLogger @@ -40,6 +39,7 @@ import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.Translator import info.nightscout.androidaps.utils.alertDialogs.OKDialog +import info.nightscout.interfaces.BuildHelper import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventNSClientRestart diff --git a/ui/src/main/java/info/nightscout/ui/defaultProfile/DefaultProfile.kt b/ui/src/main/java/info/nightscout/ui/defaultProfile/DefaultProfile.kt index 227b65753e..e9d7e5ffd6 100644 --- a/ui/src/main/java/info/nightscout/ui/defaultProfile/DefaultProfile.kt +++ b/ui/src/main/java/info/nightscout/ui/defaultProfile/DefaultProfile.kt @@ -1,7 +1,7 @@ package info.nightscout.ui.defaultProfile import info.nightscout.androidaps.data.PureProfile -import info.nightscout.androidaps.extensions.pureProfileFromJson +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.utils.DateUtil diff --git a/ui/src/main/java/info/nightscout/ui/defaultProfile/DefaultProfileDPV.kt b/ui/src/main/java/info/nightscout/ui/defaultProfile/DefaultProfileDPV.kt index 72b50cb342..d98f68addd 100644 --- a/ui/src/main/java/info/nightscout/ui/defaultProfile/DefaultProfileDPV.kt +++ b/ui/src/main/java/info/nightscout/ui/defaultProfile/DefaultProfileDPV.kt @@ -2,7 +2,7 @@ package info.nightscout.ui.defaultProfile import dagger.android.HasAndroidInjector import info.nightscout.androidaps.data.PureProfile -import info.nightscout.androidaps.extensions.pureProfileFromJson +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.utils.DateUtil diff --git a/ui/src/main/java/info/nightscout/ui/dialogs/CareDialog.kt b/ui/src/main/java/info/nightscout/ui/dialogs/CareDialog.kt index 8ce8319783..eb0a64bb8a 100644 --- a/ui/src/main/java/info/nightscout/ui/dialogs/CareDialog.kt +++ b/ui/src/main/java/info/nightscout/ui/dialogs/CareDialog.kt @@ -62,6 +62,7 @@ class CareDialog : DialogFragmentWithDate() { } private var options: EventType = EventType.BGCHECK + //private var valuesWithUnit = mutableListOf() private var valuesWithUnit = mutableListOf() @@ -88,8 +89,10 @@ class CareDialog : DialogFragmentWithDate() { savedInstanceState.putInt("options", options.ordinal) } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View { + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { onCreateViewGeneral() _binding = DialogCareBinding.inflate(inflater, container, false) return binding.root @@ -103,24 +106,28 @@ class CareDialog : DialogFragmentWithDate() { options = EventType.values()[savedInstanceState.getInt("options", 0)] } - binding.icon.setImageResource(when (options) { - EventType.BGCHECK -> R.drawable.ic_cp_bgcheck - EventType.SENSOR_INSERT -> R.drawable.ic_cp_cgm_insert - EventType.BATTERY_CHANGE -> R.drawable.ic_cp_pump_battery - EventType.NOTE -> R.drawable.ic_cp_note - EventType.EXERCISE -> R.drawable.ic_cp_exercise - EventType.QUESTION -> R.drawable.ic_cp_question - EventType.ANNOUNCEMENT -> R.drawable.ic_cp_announcement - }) - binding.title.text = rh.gs(when (options) { - EventType.BGCHECK -> R.string.careportal_bgcheck - EventType.SENSOR_INSERT -> R.string.careportal_cgmsensorinsert - EventType.BATTERY_CHANGE -> R.string.careportal_pumpbatterychange - EventType.NOTE -> R.string.careportal_note - EventType.EXERCISE -> R.string.careportal_exercise - EventType.QUESTION -> R.string.careportal_question - EventType.ANNOUNCEMENT -> R.string.careportal_announcement - }) + binding.icon.setImageResource( + when (options) { + EventType.BGCHECK -> R.drawable.ic_cp_bgcheck + EventType.SENSOR_INSERT -> R.drawable.ic_cp_cgm_insert + EventType.BATTERY_CHANGE -> R.drawable.ic_cp_pump_battery + EventType.NOTE -> R.drawable.ic_cp_note + EventType.EXERCISE -> R.drawable.ic_cp_exercise + EventType.QUESTION -> R.drawable.ic_cp_question + EventType.ANNOUNCEMENT -> R.drawable.ic_cp_announcement + } + ) + binding.title.text = rh.gs( + when (options) { + EventType.BGCHECK -> R.string.careportal_bgcheck + EventType.SENSOR_INSERT -> R.string.careportal_cgmsensorinsert + EventType.BATTERY_CHANGE -> R.string.careportal_pumpbatterychange + EventType.NOTE -> R.string.careportal_note + EventType.EXERCISE -> R.string.careportal_exercise + EventType.QUESTION -> R.string.careportal_question + EventType.ANNOUNCEMENT -> R.string.careportal_announcement + } + ) when (options) { EventType.QUESTION, @@ -157,15 +164,21 @@ class CareDialog : DialogFragmentWithDate() { if (profileFunction.getUnits() == GlucoseUnit.MMOL) { binding.bgUnits.text = rh.gs(R.string.mmol) - binding.bg.setParams(savedInstanceState?.getDouble("bg") - ?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok, bgTextWatcher) + binding.bg.setParams( + savedInstanceState?.getDouble("bg") + ?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok, bgTextWatcher + ) } else { binding.bgUnits.text = rh.gs(R.string.mgdl) - binding.bg.setParams(savedInstanceState?.getDouble("bg") - ?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, bgTextWatcher) + binding.bg.setParams( + savedInstanceState?.getDouble("bg") + ?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, bgTextWatcher + ) } - binding.duration.setParams(savedInstanceState?.getDouble("duration") - ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, binding.okcancel.ok) + binding.duration.setParams( + savedInstanceState?.getDouble("duration") + ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, binding.okcancel.ok + ) if (options == EventType.NOTE || options == EventType.QUESTION || options == EventType.ANNOUNCEMENT || options == EventType.EXERCISE) binding.notesLayout.root.visibility = View.VISIBLE // independent to preferences binding.bgLabel.labelFor = binding.bg.editTextId @@ -215,7 +228,7 @@ class CareDialog : DialogFragmentWithDate() { if (options == EventType.NOTE || options == EventType.EXERCISE) { actions.add(rh.gs(R.string.duration_label) + ": " + rh.gs(R.string.format_mins, binding.duration.value.toInt())) therapyEvent.duration = T.mins(binding.duration.value.toLong()).msecs() - valuesWithUnit.add(ValueWithUnit.Minute(binding.duration.value.toInt()).takeIf { !binding.duration.value.equals(0.0) } ) + valuesWithUnit.add(ValueWithUnit.Minute(binding.duration.value.toInt()).takeIf { !binding.duration.value.equals(0.0) }) } val notes = binding.notesLayout.notes.text.toString() if (notes.isNotEmpty()) { @@ -227,7 +240,7 @@ class CareDialog : DialogFragmentWithDate() { therapyEvent.enteredBy = enteredBy - val source = when (options) { + val source = when (options) { EventType.BGCHECK -> UserEntry.Sources.BgCheck EventType.SENSOR_INSERT -> UserEntry.Sources.SensorInsert EventType.BATTERY_CHANGE -> UserEntry.Sources.BatteryChange diff --git a/ui/src/main/java/info/nightscout/ui/dialogs/WizardInfoDialog.kt b/ui/src/main/java/info/nightscout/ui/dialogs/WizardInfoDialog.kt index c7cac95f15..4d93276d30 100644 --- a/ui/src/main/java/info/nightscout/ui/dialogs/WizardInfoDialog.kt +++ b/ui/src/main/java/info/nightscout/ui/dialogs/WizardInfoDialog.kt @@ -7,7 +7,6 @@ import android.view.ViewGroup import android.view.Window import android.view.WindowManager import dagger.android.support.DaggerDialogFragment -import info.nightscout.interfaces.Constants import info.nightscout.androidaps.database.entities.BolusCalculatorResult import info.nightscout.androidaps.extensions.bolusCalculatorResultFromJson import info.nightscout.androidaps.extensions.toJson @@ -15,6 +14,7 @@ import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.interfaces.Constants import info.nightscout.ui.R import info.nightscout.ui.databinding.DialogWizardinfoBinding import org.json.JSONObject diff --git a/ui/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt b/ui/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt index 76169aff12..c8c0e6e0e3 100644 --- a/ui/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt +++ b/ui/src/test/java/info/nightscout/androidaps/TestBaseWithProfile.kt @@ -6,16 +6,16 @@ import dagger.android.HasAndroidInjector import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.database.embedments.InsulinConfiguration import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch -import info.nightscout.androidaps.extensions.pureProfileFromJson import info.nightscout.androidaps.interfaces.ActivePlugin -import info.nightscout.interfaces.Config import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileStore -import info.nightscout.rx.bus.RxBus +import info.nightscout.androidaps.interfaces.ResourceHelper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.androidaps.interfaces.ResourceHelper +import info.nightscout.androidaps.utils.extensions.pureProfileFromJson +import info.nightscout.interfaces.Config +import info.nightscout.rx.bus.RxBus import org.json.JSONObject import org.junit.Before import org.mockito.ArgumentMatchers.anyDouble diff --git a/wear/build.gradle b/wear/build.gradle index 3fede2268c..4a0f6de6fa 100644 --- a/wear/build.gradle +++ b/wear/build.gradle @@ -10,11 +10,14 @@ buildscript { classpath 'com.hiya:jacoco-android:0.2' } } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' + +plugins { + id 'com.android.application' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/android_dependencies.gradle"